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
25static void (*gPrevNewHandler)();
26
27extern "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
39static SkOSWindow* gCurrOSWin;
40static EventTargetRef gEventTarget;
41static EventQueueRef gCurrEventQ;
42
43static 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
76static void set_bindingside(HISideBinding* side, HIViewRef parent, HIBindingKind kind) {
77    side->toView = parent;
78    side->kind = kind;
79    side->offset = 0;
80}
81
82static void set_axisscale(HIAxisScale* axis, HIViewRef parent) {
83    axis->toView = parent;
84    axis->kind = kHILayoutScaleAbsolute;
85    axis->ratio = 1;
86}
87
88static void set_axisposition(HIAxisPosition* pos, HIViewRef parent, HIPositionKind kind) {
89    pos->toView = parent;
90    pos->kind = kind;
91    pos->offset = 0;
92}
93
94SkOSWindow::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
161void 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
187void 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
205void SkOSWindow::onHandleInval(const SkIRect& r)
206{
207    (new SkEvent("inval-imageview", this->getSinkID()))->post();
208}
209
210bool 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
227void SkOSWindow::onSetTitle(const char title[])
228{
229    CFStringRef str = CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8);
230    SetWindowTitleWithCFString((WindowRef)fHWND, str);
231    CFRelease(str);
232}
233
234void SkOSWindow::onAddMenu(const SkOSMenu* sk_menu)
235{
236}
237
238static 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
250enum {
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
271static 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
302static 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
325pascal 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
433void SkEvent::SignalNonEmptyQueue()
434{
435    post_skmacevent();
436//    SkDebugf("signal nonempty\n");
437}
438
439static TMTask    gTMTaskRec;
440static TMTask*    gTMTaskPtr;
441
442static void sk_timer_proc(TMTask* rec)
443{
444    SkEvent::ServiceQueueTimer();
445//    SkDebugf("timer task fired\n");
446}
447
448void 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
469AGLContext 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
502bool 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
534void SkOSWindow::detach() {
535    aglSetWindowRef((AGLContext)fAGLCtx, NULL);
536}
537
538void SkOSWindow::present() {
539    aglSwapBuffers((AGLContext)fAGLCtx);
540}
541
542#endif
543