|
1 /* |
|
2 * Copyright 2011 Google Inc. |
|
3 * |
|
4 * Use of this source code is governed by a BSD-style license that can be |
|
5 * found in the LICENSE file. |
|
6 */ |
|
7 |
|
8 #include "SkTypes.h" |
|
9 |
|
10 #if defined(SK_BUILD_FOR_MAC) |
|
11 |
|
12 #include <AGL/agl.h> |
|
13 |
|
14 #include <Carbon/Carbon.h> |
|
15 #include "SkCGUtils.h" |
|
16 |
|
17 #include "SkWindow.h" |
|
18 #include "SkCanvas.h" |
|
19 #include "SkOSMenu.h" |
|
20 #include "SkTime.h" |
|
21 |
|
22 #include "SkGraphics.h" |
|
23 #include <new.h> |
|
24 |
|
25 static void (*gPrevNewHandler)(); |
|
26 |
|
27 extern "C" { |
|
28 static void sk_new_handler() |
|
29 { |
|
30 if (SkGraphics::SetFontCacheUsed(0)) |
|
31 return; |
|
32 if (gPrevNewHandler) |
|
33 gPrevNewHandler(); |
|
34 else |
|
35 sk_throw(); |
|
36 } |
|
37 } |
|
38 |
|
39 static SkOSWindow* gCurrOSWin; |
|
40 static EventTargetRef gEventTarget; |
|
41 static EventQueueRef gCurrEventQ; |
|
42 |
|
43 static OSStatus MyDrawEventHandler(EventHandlerCallRef myHandler, |
|
44 EventRef event, void *userData) { |
|
45 // NOTE: GState is save/restored by the HIView system doing the callback, |
|
46 // so the draw handler doesn't need to do it |
|
47 |
|
48 OSStatus status = noErr; |
|
49 CGContextRef context; |
|
50 HIRect bounds; |
|
51 |
|
52 // Get the CGContextRef |
|
53 status = GetEventParameter (event, kEventParamCGContextRef, |
|
54 typeCGContextRef, NULL, |
|
55 sizeof (CGContextRef), |
|
56 NULL, |
|
57 &context); |
|
58 |
|
59 if (status != noErr) { |
|
60 SkDebugf("Got error %d getting the context!\n", status); |
|
61 return status; |
|
62 } |
|
63 |
|
64 // Get the bounding rectangle |
|
65 HIViewGetBounds ((HIViewRef) userData, &bounds); |
|
66 |
|
67 gCurrOSWin->doPaint(context); |
|
68 return status; |
|
69 } |
|
70 |
|
71 #define SK_MacEventClass FOUR_CHAR_CODE('SKec') |
|
72 #define SK_MacEventKind FOUR_CHAR_CODE('SKek') |
|
73 #define SK_MacEventParamName FOUR_CHAR_CODE('SKev') |
|
74 #define SK_MacEventSinkIDParamName FOUR_CHAR_CODE('SKes') |
|
75 |
|
76 static void set_bindingside(HISideBinding* side, HIViewRef parent, HIBindingKind kind) { |
|
77 side->toView = parent; |
|
78 side->kind = kind; |
|
79 side->offset = 0; |
|
80 } |
|
81 |
|
82 static void set_axisscale(HIAxisScale* axis, HIViewRef parent) { |
|
83 axis->toView = parent; |
|
84 axis->kind = kHILayoutScaleAbsolute; |
|
85 axis->ratio = 1; |
|
86 } |
|
87 |
|
88 static void set_axisposition(HIAxisPosition* pos, HIViewRef parent, HIPositionKind kind) { |
|
89 pos->toView = parent; |
|
90 pos->kind = kind; |
|
91 pos->offset = 0; |
|
92 } |
|
93 |
|
94 SkOSWindow::SkOSWindow(void* hWnd) : fHWND(hWnd), fAGLCtx(NULL) |
|
95 { |
|
96 OSStatus result; |
|
97 WindowRef wr = (WindowRef)hWnd; |
|
98 |
|
99 HIViewRef imageView, parent; |
|
100 HIViewRef rootView = HIViewGetRoot(wr); |
|
101 HIViewFindByID(rootView, kHIViewWindowContentID, &parent); |
|
102 result = HIImageViewCreate(NULL, &imageView); |
|
103 SkASSERT(result == noErr); |
|
104 |
|
105 result = HIViewAddSubview(parent, imageView); |
|
106 SkASSERT(result == noErr); |
|
107 |
|
108 fHVIEW = imageView; |
|
109 |
|
110 HIViewSetVisible(imageView, true); |
|
111 HIViewPlaceInSuperviewAt(imageView, 0, 0); |
|
112 |
|
113 if (true) { |
|
114 HILayoutInfo layout; |
|
115 layout.version = kHILayoutInfoVersionZero; |
|
116 set_bindingside(&layout.binding.left, parent, kHILayoutBindLeft); |
|
117 set_bindingside(&layout.binding.top, parent, kHILayoutBindTop); |
|
118 set_bindingside(&layout.binding.right, parent, kHILayoutBindRight); |
|
119 set_bindingside(&layout.binding.bottom, parent, kHILayoutBindBottom); |
|
120 set_axisscale(&layout.scale.x, parent); |
|
121 set_axisscale(&layout.scale.y, parent); |
|
122 set_axisposition(&layout.position.x, parent, kHILayoutPositionLeft); |
|
123 set_axisposition(&layout.position.y, rootView, kHILayoutPositionTop); |
|
124 HIViewSetLayoutInfo(imageView, &layout); |
|
125 } |
|
126 |
|
127 HIImageViewSetOpaque(imageView, true); |
|
128 HIImageViewSetScaleToFit(imageView, false); |
|
129 |
|
130 static const EventTypeSpec gTypes[] = { |
|
131 { kEventClassKeyboard, kEventRawKeyDown }, |
|
132 { kEventClassKeyboard, kEventRawKeyUp }, |
|
133 { kEventClassMouse, kEventMouseDown }, |
|
134 { kEventClassMouse, kEventMouseDragged }, |
|
135 { kEventClassMouse, kEventMouseMoved }, |
|
136 { kEventClassMouse, kEventMouseUp }, |
|
137 { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }, |
|
138 { kEventClassWindow, kEventWindowBoundsChanged }, |
|
139 // { kEventClassWindow, kEventWindowDrawContent }, |
|
140 { SK_MacEventClass, SK_MacEventKind } |
|
141 }; |
|
142 |
|
143 EventHandlerUPP handlerUPP = NewEventHandlerUPP(SkOSWindow::EventHandler); |
|
144 int count = SK_ARRAY_COUNT(gTypes); |
|
145 |
|
146 result = InstallEventHandler(GetWindowEventTarget(wr), handlerUPP, |
|
147 count, gTypes, this, nil); |
|
148 SkASSERT(result == noErr); |
|
149 |
|
150 gCurrOSWin = this; |
|
151 gCurrEventQ = GetCurrentEventQueue(); |
|
152 gEventTarget = GetWindowEventTarget(wr); |
|
153 |
|
154 static bool gOnce = true; |
|
155 if (gOnce) { |
|
156 gOnce = false; |
|
157 gPrevNewHandler = set_new_handler(sk_new_handler); |
|
158 } |
|
159 } |
|
160 |
|
161 void SkOSWindow::doPaint(void* ctx) |
|
162 { |
|
163 #if 0 |
|
164 this->update(NULL); |
|
165 |
|
166 const SkBitmap& bm = this->getBitmap(); |
|
167 CGImageRef img = SkCreateCGImageRef(bm); |
|
168 |
|
169 if (img) { |
|
170 CGRect r = CGRectMake(0, 0, bm.width(), bm.height()); |
|
171 |
|
172 CGContextRef cg = reinterpret_cast<CGContextRef>(ctx); |
|
173 |
|
174 CGContextSaveGState(cg); |
|
175 CGContextTranslateCTM(cg, 0, r.size.height); |
|
176 CGContextScaleCTM(cg, 1, -1); |
|
177 |
|
178 CGContextDrawImage(cg, r, img); |
|
179 |
|
180 CGContextRestoreGState(cg); |
|
181 |
|
182 CGImageRelease(img); |
|
183 } |
|
184 #endif |
|
185 } |
|
186 |
|
187 void SkOSWindow::updateSize() |
|
188 { |
|
189 Rect r; |
|
190 |
|
191 GetWindowBounds((WindowRef)fHWND, kWindowContentRgn, &r); |
|
192 this->resize(r.right - r.left, r.bottom - r.top); |
|
193 |
|
194 #if 0 |
|
195 HIRect frame; |
|
196 HIViewRef imageView = (HIViewRef)getHVIEW(); |
|
197 HIViewRef parent = HIViewGetSuperview(imageView); |
|
198 |
|
199 HIViewGetBounds(imageView, &frame); |
|
200 SkDebugf("------ %d bounds %g %g %g %g\n", r.right - r.left, |
|
201 frame.origin.x, frame.origin.y, frame.size.width, frame.size.height); |
|
202 #endif |
|
203 } |
|
204 |
|
205 void SkOSWindow::onHandleInval(const SkIRect& r) |
|
206 { |
|
207 (new SkEvent("inval-imageview", this->getSinkID()))->post(); |
|
208 } |
|
209 |
|
210 bool SkOSWindow::onEvent(const SkEvent& evt) { |
|
211 if (evt.isType("inval-imageview")) { |
|
212 this->update(NULL); |
|
213 |
|
214 SkEvent query("ignore-window-bitmap"); |
|
215 if (!this->doQuery(&query) || !query.getFast32()) { |
|
216 const SkBitmap& bm = this->getBitmap(); |
|
217 |
|
218 CGImageRef img = SkCreateCGImageRef(bm); |
|
219 HIImageViewSetImage((HIViewRef)getHVIEW(), img); |
|
220 CGImageRelease(img); |
|
221 } |
|
222 return true; |
|
223 } |
|
224 return INHERITED::onEvent(evt); |
|
225 } |
|
226 |
|
227 void SkOSWindow::onSetTitle(const char title[]) |
|
228 { |
|
229 CFStringRef str = CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8); |
|
230 SetWindowTitleWithCFString((WindowRef)fHWND, str); |
|
231 CFRelease(str); |
|
232 } |
|
233 |
|
234 void SkOSWindow::onAddMenu(const SkOSMenu* sk_menu) |
|
235 { |
|
236 } |
|
237 |
|
238 static void getparam(EventRef inEvent, OSType name, OSType type, UInt32 size, void* data) |
|
239 { |
|
240 EventParamType actualType; |
|
241 UInt32 actualSize; |
|
242 OSStatus status; |
|
243 |
|
244 status = GetEventParameter(inEvent, name, type, &actualType, size, &actualSize, data); |
|
245 SkASSERT(status == noErr); |
|
246 SkASSERT(actualType == type); |
|
247 SkASSERT(actualSize == size); |
|
248 } |
|
249 |
|
250 enum { |
|
251 SK_MacReturnKey = 36, |
|
252 SK_MacDeleteKey = 51, |
|
253 SK_MacEndKey = 119, |
|
254 SK_MacLeftKey = 123, |
|
255 SK_MacRightKey = 124, |
|
256 SK_MacDownKey = 125, |
|
257 SK_MacUpKey = 126, |
|
258 |
|
259 SK_Mac0Key = 0x52, |
|
260 SK_Mac1Key = 0x53, |
|
261 SK_Mac2Key = 0x54, |
|
262 SK_Mac3Key = 0x55, |
|
263 SK_Mac4Key = 0x56, |
|
264 SK_Mac5Key = 0x57, |
|
265 SK_Mac6Key = 0x58, |
|
266 SK_Mac7Key = 0x59, |
|
267 SK_Mac8Key = 0x5b, |
|
268 SK_Mac9Key = 0x5c |
|
269 }; |
|
270 |
|
271 static SkKey raw2key(UInt32 raw) |
|
272 { |
|
273 static const struct { |
|
274 UInt32 fRaw; |
|
275 SkKey fKey; |
|
276 } gKeys[] = { |
|
277 { SK_MacUpKey, kUp_SkKey }, |
|
278 { SK_MacDownKey, kDown_SkKey }, |
|
279 { SK_MacLeftKey, kLeft_SkKey }, |
|
280 { SK_MacRightKey, kRight_SkKey }, |
|
281 { SK_MacReturnKey, kOK_SkKey }, |
|
282 { SK_MacDeleteKey, kBack_SkKey }, |
|
283 { SK_MacEndKey, kEnd_SkKey }, |
|
284 { SK_Mac0Key, k0_SkKey }, |
|
285 { SK_Mac1Key, k1_SkKey }, |
|
286 { SK_Mac2Key, k2_SkKey }, |
|
287 { SK_Mac3Key, k3_SkKey }, |
|
288 { SK_Mac4Key, k4_SkKey }, |
|
289 { SK_Mac5Key, k5_SkKey }, |
|
290 { SK_Mac6Key, k6_SkKey }, |
|
291 { SK_Mac7Key, k7_SkKey }, |
|
292 { SK_Mac8Key, k8_SkKey }, |
|
293 { SK_Mac9Key, k9_SkKey } |
|
294 }; |
|
295 |
|
296 for (unsigned i = 0; i < SK_ARRAY_COUNT(gKeys); i++) |
|
297 if (gKeys[i].fRaw == raw) |
|
298 return gKeys[i].fKey; |
|
299 return kNONE_SkKey; |
|
300 } |
|
301 |
|
302 static void post_skmacevent() |
|
303 { |
|
304 EventRef ref; |
|
305 OSStatus status = CreateEvent(nil, SK_MacEventClass, SK_MacEventKind, 0, 0, &ref); |
|
306 SkASSERT(status == noErr); |
|
307 |
|
308 #if 0 |
|
309 status = SetEventParameter(ref, SK_MacEventParamName, SK_MacEventParamName, sizeof(evt), &evt); |
|
310 SkASSERT(status == noErr); |
|
311 status = SetEventParameter(ref, SK_MacEventSinkIDParamName, SK_MacEventSinkIDParamName, sizeof(sinkID), &sinkID); |
|
312 SkASSERT(status == noErr); |
|
313 #endif |
|
314 |
|
315 EventTargetRef target = gEventTarget; |
|
316 SetEventParameter(ref, kEventParamPostTarget, typeEventTargetRef, sizeof(target), &target); |
|
317 SkASSERT(status == noErr); |
|
318 |
|
319 status = PostEventToQueue(gCurrEventQ, ref, kEventPriorityStandard); |
|
320 SkASSERT(status == noErr); |
|
321 |
|
322 ReleaseEvent(ref); |
|
323 } |
|
324 |
|
325 pascal OSStatus SkOSWindow::EventHandler( EventHandlerCallRef inHandler, EventRef inEvent, void* userData ) |
|
326 { |
|
327 SkOSWindow* win = (SkOSWindow*)userData; |
|
328 OSStatus result = eventNotHandledErr; |
|
329 UInt32 wClass = GetEventClass(inEvent); |
|
330 UInt32 wKind = GetEventKind(inEvent); |
|
331 |
|
332 gCurrOSWin = win; // will need to be in TLS. Set this so PostEvent will work |
|
333 |
|
334 switch (wClass) { |
|
335 case kEventClassMouse: { |
|
336 Point pt; |
|
337 getparam(inEvent, kEventParamMouseLocation, typeQDPoint, sizeof(pt), &pt); |
|
338 SetPortWindowPort((WindowRef)win->getHWND()); |
|
339 GlobalToLocal(&pt); |
|
340 |
|
341 switch (wKind) { |
|
342 case kEventMouseDown: |
|
343 if (win->handleClick(pt.h, pt.v, Click::kDown_State)) { |
|
344 result = noErr; |
|
345 } |
|
346 break; |
|
347 case kEventMouseMoved: |
|
348 // fall through |
|
349 case kEventMouseDragged: |
|
350 (void)win->handleClick(pt.h, pt.v, Click::kMoved_State); |
|
351 // result = noErr; |
|
352 break; |
|
353 case kEventMouseUp: |
|
354 (void)win->handleClick(pt.h, pt.v, Click::kUp_State); |
|
355 // result = noErr; |
|
356 break; |
|
357 default: |
|
358 break; |
|
359 } |
|
360 break; |
|
361 } |
|
362 case kEventClassKeyboard: |
|
363 if (wKind == kEventRawKeyDown) { |
|
364 UInt32 raw; |
|
365 getparam(inEvent, kEventParamKeyCode, typeUInt32, sizeof(raw), &raw); |
|
366 SkKey key = raw2key(raw); |
|
367 if (key != kNONE_SkKey) |
|
368 (void)win->handleKey(key); |
|
369 } else if (wKind == kEventRawKeyUp) { |
|
370 UInt32 raw; |
|
371 getparam(inEvent, kEventParamKeyCode, typeUInt32, sizeof(raw), &raw); |
|
372 SkKey key = raw2key(raw); |
|
373 if (key != kNONE_SkKey) |
|
374 (void)win->handleKeyUp(key); |
|
375 } |
|
376 break; |
|
377 case kEventClassTextInput: |
|
378 if (wKind == kEventTextInputUnicodeForKeyEvent) { |
|
379 UInt16 uni; |
|
380 getparam(inEvent, kEventParamTextInputSendText, typeUnicodeText, sizeof(uni), &uni); |
|
381 win->handleChar(uni); |
|
382 } |
|
383 break; |
|
384 case kEventClassWindow: |
|
385 switch (wKind) { |
|
386 case kEventWindowBoundsChanged: |
|
387 win->updateSize(); |
|
388 break; |
|
389 case kEventWindowDrawContent: { |
|
390 CGContextRef cg; |
|
391 result = GetEventParameter(inEvent, |
|
392 kEventParamCGContextRef, |
|
393 typeCGContextRef, |
|
394 NULL, |
|
395 sizeof (CGContextRef), |
|
396 NULL, |
|
397 &cg); |
|
398 if (result != 0) { |
|
399 cg = NULL; |
|
400 } |
|
401 win->doPaint(cg); |
|
402 break; |
|
403 } |
|
404 default: |
|
405 break; |
|
406 } |
|
407 break; |
|
408 case SK_MacEventClass: { |
|
409 SkASSERT(wKind == SK_MacEventKind); |
|
410 if (SkEvent::ProcessEvent()) { |
|
411 post_skmacevent(); |
|
412 } |
|
413 #if 0 |
|
414 SkEvent* evt; |
|
415 SkEventSinkID sinkID; |
|
416 getparam(inEvent, SK_MacEventParamName, SK_MacEventParamName, sizeof(evt), &evt); |
|
417 getparam(inEvent, SK_MacEventSinkIDParamName, SK_MacEventSinkIDParamName, sizeof(sinkID), &sinkID); |
|
418 #endif |
|
419 result = noErr; |
|
420 break; |
|
421 } |
|
422 default: |
|
423 break; |
|
424 } |
|
425 if (result == eventNotHandledErr) { |
|
426 result = CallNextEventHandler(inHandler, inEvent); |
|
427 } |
|
428 return result; |
|
429 } |
|
430 |
|
431 /////////////////////////////////////////////////////////////////////////////////////// |
|
432 |
|
433 void SkEvent::SignalNonEmptyQueue() |
|
434 { |
|
435 post_skmacevent(); |
|
436 // SkDebugf("signal nonempty\n"); |
|
437 } |
|
438 |
|
439 static TMTask gTMTaskRec; |
|
440 static TMTask* gTMTaskPtr; |
|
441 |
|
442 static void sk_timer_proc(TMTask* rec) |
|
443 { |
|
444 SkEvent::ServiceQueueTimer(); |
|
445 // SkDebugf("timer task fired\n"); |
|
446 } |
|
447 |
|
448 void SkEvent::SignalQueueTimer(SkMSec delay) |
|
449 { |
|
450 if (gTMTaskPtr) |
|
451 { |
|
452 RemoveTimeTask((QElem*)gTMTaskPtr); |
|
453 DisposeTimerUPP(gTMTaskPtr->tmAddr); |
|
454 gTMTaskPtr = nil; |
|
455 } |
|
456 if (delay) |
|
457 { |
|
458 gTMTaskPtr = &gTMTaskRec; |
|
459 memset(gTMTaskPtr, 0, sizeof(gTMTaskRec)); |
|
460 gTMTaskPtr->tmAddr = NewTimerUPP(sk_timer_proc); |
|
461 OSErr err = InstallTimeTask((QElem*)gTMTaskPtr); |
|
462 // SkDebugf("installtimetask of %d returned %d\n", delay, err); |
|
463 PrimeTimeTask((QElem*)gTMTaskPtr, delay); |
|
464 } |
|
465 } |
|
466 |
|
467 #define USE_MSAA 0 |
|
468 |
|
469 AGLContext create_gl(WindowRef wref) |
|
470 { |
|
471 GLint major, minor; |
|
472 AGLContext ctx; |
|
473 |
|
474 aglGetVersion(&major, &minor); |
|
475 SkDebugf("---- agl version %d %d\n", major, minor); |
|
476 |
|
477 const GLint pixelAttrs[] = { |
|
478 AGL_RGBA, |
|
479 AGL_STENCIL_SIZE, 8, |
|
480 #if USE_MSAA |
|
481 AGL_SAMPLE_BUFFERS_ARB, 1, |
|
482 AGL_MULTISAMPLE, |
|
483 AGL_SAMPLES_ARB, 8, |
|
484 #endif |
|
485 AGL_ACCELERATED, |
|
486 AGL_DOUBLEBUFFER, |
|
487 AGL_NONE |
|
488 }; |
|
489 AGLPixelFormat format = aglChoosePixelFormat(NULL, 0, pixelAttrs); |
|
490 //AGLPixelFormat format = aglCreatePixelFormat(pixelAttrs); |
|
491 SkDebugf("----- agl format %p\n", format); |
|
492 ctx = aglCreateContext(format, NULL); |
|
493 SkDebugf("----- agl context %p\n", ctx); |
|
494 aglDestroyPixelFormat(format); |
|
495 |
|
496 static const GLint interval = 1; |
|
497 aglSetInteger(ctx, AGL_SWAP_INTERVAL, &interval); |
|
498 aglSetCurrentContext(ctx); |
|
499 return ctx; |
|
500 } |
|
501 |
|
502 bool SkOSWindow::attach(SkBackEndTypes /* attachType */) |
|
503 { |
|
504 if (NULL == fAGLCtx) { |
|
505 fAGLCtx = create_gl((WindowRef)fHWND); |
|
506 if (NULL == fAGLCtx) { |
|
507 return false; |
|
508 } |
|
509 } |
|
510 |
|
511 GLboolean success = true; |
|
512 |
|
513 int width, height; |
|
514 |
|
515 success = aglSetWindowRef((AGLContext)fAGLCtx, (WindowRef)fHWND); |
|
516 width = this->width(); |
|
517 height = this->height(); |
|
518 |
|
519 GLenum err = aglGetError(); |
|
520 if (err) { |
|
521 SkDebugf("---- aglSetWindowRef %d %d %s [%d %d]\n", success, err, |
|
522 aglErrorString(err), width, height); |
|
523 } |
|
524 |
|
525 if (success) { |
|
526 glViewport(0, 0, width, height); |
|
527 glClearColor(0, 0, 0, 0); |
|
528 glClearStencil(0); |
|
529 glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); |
|
530 } |
|
531 return success; |
|
532 } |
|
533 |
|
534 void SkOSWindow::detach() { |
|
535 aglSetWindowRef((AGLContext)fAGLCtx, NULL); |
|
536 } |
|
537 |
|
538 void SkOSWindow::present() { |
|
539 aglSwapBuffers((AGLContext)fAGLCtx); |
|
540 } |
|
541 |
|
542 #endif |