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