1 /*
   2  * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #import "common.h"
  27 #import "com_sun_glass_events_DndEvent.h"
  28 #import "com_sun_glass_events_KeyEvent.h"
  29 #import "com_sun_glass_events_MouseEvent.h"
  30 #import "com_sun_glass_ui_View_Capability.h"
  31 #import "com_sun_glass_ui_mac_MacGestureSupport.h"
  32 #import "GlassKey.h"
  33 #import "GlassMacros.h"
  34 #import "GlassView3D.h"
  35 #import "GlassLayer3D.h"
  36 #import "GlassApplication.h"
  37 
  38 //#define VERBOSE
  39 #ifndef VERBOSE
  40     #define LOG(MSG, ...)
  41 #else
  42     #define LOG(MSG, ...) GLASS_LOG(MSG, ## __VA_ARGS__);
  43 #endif
  44 
  45 //#define MOUSEVERBOSE
  46 #ifndef MOUSEVERBOSE
  47     #define MOUSELOG(MSG, ...)
  48 #else
  49     #define MOUSELOG(MSG, ...) GLASS_LOG(MSG, ## __VA_ARGS__);
  50 #endif
  51 
  52 //#define KEYVERBOSE
  53 #ifndef KEYVERBOSE
  54     #define KEYLOG(MSG, ...)
  55 #else
  56     #define KEYLOG(MSG, ...) GLASS_LOG(MSG, ## __VA_ARGS__);
  57 #endif
  58 
  59 //#define DNDVERBOSE
  60 #ifndef DNDVERBOSE
  61     #define DNDLOG(MSG, ...)
  62 #else
  63     #define DNDLOG(MSG, ...) GLASS_LOG(MSG, ## __VA_ARGS__);
  64 #endif
  65 
  66 //#define IMVERBOSE
  67 #ifndef IMVERBOSE
  68     #define IMLOG(MSG, ...)
  69 #else
  70     #define IMLOG(MSG, ...) GLASS_LOG(MSG, ## __VA_ARGS__);
  71 #endif
  72 
  73 #define SHARE_GL_CONTEXT
  74 //#define DEBUG_COLORS
  75 
  76 // http://developer.apple.com/library/mac/#technotes/tn2085/_index.html
  77 //#define ENABLE_MULTITHREADED_GL
  78 
  79 @implementation GlassView3D
  80 
  81 - (CGLPixelFormatObj)_createPixelFormatWithDepth:(CGLPixelFormatAttribute)depth
  82 {
  83     CGLPixelFormatObj pix = NULL;
  84     {
  85         const CGLPixelFormatAttribute attributes[] =
  86         {
  87             kCGLPFAAccelerated,
  88             kCGLPFAColorSize, 32,
  89             kCGLPFAAlphaSize, 8,
  90             kCGLPFADepthSize, depth,
  91             kCGLPFAAllowOfflineRenderers, // lets OpenGL know this context is offline renderer aware
  92             (CGLPixelFormatAttribute)0
  93         };
  94         GLint npix = 0;
  95         CGLError err = CGLChoosePixelFormat(attributes, &pix, &npix);
  96         if (err != kCGLNoError)
  97         {
  98             NSLog(@"CGLChoosePixelFormat error: %d", err);
  99         }
 100     }
 101     return pix;
 102 }
 103 
 104 - (CGLContextObj)_createContextWithShared:(CGLContextObj)share withFormat:(CGLPixelFormatObj)format
 105 {
 106     CGLContextObj ctx = NULL;
 107     {
 108         CGLError err = CGLCreateContext(format, share, &ctx);
 109         if (err != kCGLNoError)
 110         {
 111             NSLog(@"CGLCreateContext error: %d", err);
 112         }
 113     }
 114     return ctx;
 115 }
 116 
 117 - (void)_initialize3dWithJproperties:(jobject)jproperties
 118 {
 119     GET_MAIN_JENV;
 120 
 121     int depthBits = 0;
 122     if (jproperties != NULL)
 123     {
 124         jobject k3dDepthKey = (*env)->NewObject(env, jIntegerClass, jIntegerInitMethod, com_sun_glass_ui_View_Capability_k3dDepthKeyValue);
 125         GLASS_CHECK_EXCEPTION(env);
 126         jobject k3dDepthKeyValue = (*env)->CallObjectMethod(env, jproperties, jMapGetMethod, k3dDepthKey);
 127         GLASS_CHECK_EXCEPTION(env);
 128         if (k3dDepthKeyValue != NULL)
 129         {
 130             depthBits = (*env)->CallIntMethod(env, k3dDepthKeyValue, jIntegerValueMethod);
 131             GLASS_CHECK_EXCEPTION(env);
 132         }
 133     }
 134 
 135     CGLContextObj sharedCGL = NULL;
 136     if (jproperties != NULL)
 137     {
 138         jobject sharedContextPtrKey = (*env)->NewStringUTF(env, "shareContextPtr");
 139         jobject sharedContextPtrValue = (*env)->CallObjectMethod(env, jproperties, jMapGetMethod, sharedContextPtrKey);
 140         GLASS_CHECK_EXCEPTION(env);
 141         if (sharedContextPtrValue != NULL)
 142         {
 143             jlong jsharedContextPtr = (*env)->CallLongMethod(env, sharedContextPtrValue, jLongValueMethod);
 144             GLASS_CHECK_EXCEPTION(env);
 145             if (jsharedContextPtr != 0)
 146             {
 147                 NSOpenGLContext *sharedContextNS = (NSOpenGLContext*)jlong_to_ptr(jsharedContextPtr);
 148                 sharedCGL = [sharedContextNS CGLContextObj];
 149             }
 150         }
 151     }
 152 
 153     CGLContextObj clientCGL = NULL;
 154     BOOL isSwPipe = NO;
 155 
 156     if (jproperties != NULL)
 157     {
 158         jobject contextPtrKey = (*env)->NewStringUTF(env, "contextPtr");
 159         jobject contextPtrValue = (*env)->CallObjectMethod(env, jproperties, jMapGetMethod, contextPtrKey);
 160         GLASS_CHECK_EXCEPTION(env);
 161         if (contextPtrValue != NULL)
 162         {
 163             jlong jcontextPtr = (*env)->CallLongMethod(env, contextPtrValue, jLongValueMethod);
 164             GLASS_CHECK_EXCEPTION(env);
 165             if (jcontextPtr != 0)
 166             {
 167                 NSOpenGLContext *clientContextNS = (NSOpenGLContext*)jlong_to_ptr(jcontextPtr);
 168                 clientCGL = [clientContextNS CGLContextObj];
 169             }
 170         }
 171     }
 172     if (clientCGL == NULL)
 173     {
 174         CGLPixelFormatObj clientPixelFormat = [self _createPixelFormatWithDepth:(CGLPixelFormatAttribute)depthBits];
 175         clientCGL = [self _createContextWithShared:sharedCGL withFormat:clientPixelFormat];
 176     }
 177     if (sharedCGL == NULL)
 178     {
 179         // this can happen in Rain or clients other than Prism (ie. device details do not have the shared context set)
 180         sharedCGL = clientCGL;
 181         isSwPipe = YES;
 182     }
 183 
 184     self->isHiDPIAware = NO;
 185     if (jproperties != NULL)
 186     {
 187         jobject kHiDPIAwareKey = (*env)->NewObject(env, jIntegerClass, jIntegerInitMethod, com_sun_glass_ui_View_Capability_kHiDPIAwareKeyValue);
 188         GLASS_CHECK_EXCEPTION(env);
 189         jobject kHiDPIAwareValue = (*env)->CallObjectMethod(env, jproperties, jMapGetMethod, kHiDPIAwareKey);
 190         GLASS_CHECK_EXCEPTION(env);
 191         if (kHiDPIAwareValue != NULL)
 192         {
 193             self->isHiDPIAware = (*env)->CallBooleanMethod(env, kHiDPIAwareValue, jBooleanValueMethod) ? YES : NO;
 194             GLASS_CHECK_EXCEPTION(env);
 195         }
 196     }
 197 
 198     GlassLayer3D *layer = [[GlassLayer3D alloc] initWithSharedContext:sharedCGL andClientContext:clientCGL withHiDPIAware:self->isHiDPIAware withIsSwPipe:isSwPipe];
 199 
 200     // https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/nsview_Class/Reference/NSView.html#//apple_ref/occ/instm/NSView/setWantsLayer:
 201     // the order of the following 2 calls is important: here we indicate we want a layer-hosting view
 202     {
 203         [self setLayer:layer];
 204         [self setWantsLayer:YES];
 205     }
 206 }
 207 
 208 - (id)initWithFrame:(NSRect)frame withJview:(jobject)jView withJproperties:(jobject)jproperties
 209 {
 210     LOG("GlassView3D initWithFrame:withJview:withJproperties");
 211 
 212     NSOpenGLPixelFormatAttribute pixelFormatAttributes[] =
 213     {
 214         NSOpenGLPFAAllowOfflineRenderers, // Lets OpenGL know this context is offline renderer aware
 215         (NSOpenGLPixelFormatAttribute)0
 216     };
 217     NSOpenGLPixelFormat *pFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes:pixelFormatAttributes] autorelease];
 218     if (!pFormat)
 219     {
 220         pFormat = [NSOpenGLView defaultPixelFormat];
 221         LOG("GlassView3D initWithFrame: initWithAttributes failed! Set pixel format to default pixel format");
 222     }
 223     self = [super initWithFrame:frame pixelFormat:pFormat];
 224     if (self != nil)
 225     {
 226         [self _initialize3dWithJproperties:jproperties];
 227 
 228         self->_delegate = [[GlassViewDelegate alloc] initWithView:self withJview:jView];
 229         self->_drawCounter = 0;
 230         self->_texture = 0;
 231 
 232         self->_trackingArea = [[NSTrackingArea alloc] initWithRect:frame
 233                                                            options:(NSTrackingMouseMoved | NSTrackingActiveAlways | NSTrackingInVisibleRect)
 234                                                              owner:self userInfo:nil];
 235         [self addTrackingArea: self->_trackingArea];
 236         self->nsAttrBuffer = [[NSAttributedString alloc] initWithString:@""];
 237         self->imEnabled = NO;
 238         self->shouldProcessKeyEvent = YES;
 239     }
 240     return self;
 241 }
 242 
 243 - (void)dealloc
 244 {
 245     if (self->_texture != 0)
 246     {
 247         GlassLayer3D *layer = (GlassLayer3D*)[self layer];
 248         [[layer getPainterOffscreen] bindForWidth:(GLuint)[self bounds].size.width andHeight:(GLuint)[self bounds].size.height];
 249         {
 250             glDeleteTextures(1, &self->_texture);
 251         }
 252         [[layer getPainterOffscreen] unbind];
 253     }
 254 
 255     [[self layer] release];
 256     [self->_delegate release];
 257     self->_delegate = nil;
 258 
 259     [self removeTrackingArea: self->_trackingArea];
 260     [self->_trackingArea release];
 261     self->_trackingArea = nil;
 262 
 263     [self->nsAttrBuffer release];
 264     self->nsAttrBuffer = nil;
 265 
 266     [super dealloc];
 267 }
 268 
 269 - (BOOL)becomeFirstResponder
 270 {
 271     return YES;
 272 }
 273 
 274 - (BOOL)acceptsFirstResponder
 275 {
 276     return YES;
 277 }
 278 
 279 - (BOOL)canBecomeKeyView
 280 {
 281     return YES;
 282 }
 283 
 284 - (BOOL)postsBoundsChangedNotifications
 285 {
 286     return NO;
 287 }
 288 
 289 - (BOOL)postsFrameChangedNotifications
 290 {
 291     return NO;
 292 }
 293 
 294 - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
 295 {
 296     return YES;
 297 }
 298 
 299 - (BOOL)isFlipped
 300 {
 301     return YES;
 302 }
 303 
 304 - (BOOL)isOpaque
 305 {
 306     return NO;
 307 }
 308 
 309 - (BOOL)mouseDownCanMoveWindow
 310 {
 311     return NO;
 312 }
 313 
 314 // also called when closing window, when [self window] == nil
 315 - (void)viewDidMoveToWindow
 316 {
 317     if ([self window] != nil)
 318     {
 319         GlassLayer3D *layer = (GlassLayer3D*)[self layer];
 320         [[layer getPainterOffscreen] setBackgroundColor:[[[self window] backgroundColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace]];
 321     }
 322 
 323     [self->_delegate viewDidMoveToWindow];
 324 }
 325 
 326 - (void)setFrameOrigin:(NSPoint)newOrigin
 327 {
 328     [super setFrameOrigin:newOrigin];
 329     [self->_delegate setFrameOrigin:newOrigin];
 330 }
 331 
 332 - (void)setFrameSize:(NSSize)newSize
 333 {
 334     [super setFrameSize:newSize];
 335     [self->_delegate setFrameSize:newSize];
 336 }
 337 
 338 - (void)setFrame:(NSRect)frameRect
 339 {
 340     [super setFrame:frameRect];
 341     [self->_delegate setFrame:frameRect];
 342 }
 343 
 344 - (void)updateTrackingAreas
 345 {
 346     [super updateTrackingAreas];
 347     [self->_delegate updateTrackingAreas];
 348 }
 349 
 350 - (void)mouseEntered:(NSEvent *)theEvent
 351 {
 352     MOUSELOG("mouseEntered");
 353     [self->_delegate sendJavaMouseEvent:theEvent];
 354 }
 355 
 356 - (void)mouseMoved:(NSEvent *)theEvent
 357 {
 358     MOUSELOG("mouseMoved");
 359     [self->_delegate sendJavaMouseEvent:theEvent];
 360 }
 361 
 362 - (void)mouseExited:(NSEvent *)theEvent
 363 {
 364     MOUSELOG("mouseExited");
 365     [self->_delegate sendJavaMouseEvent:theEvent];
 366 }
 367 
 368 - (void)mouseDown:(NSEvent *)theEvent
 369 {
 370     MOUSELOG("mouseDown");
 371     // First check if system Input Method Engine needs to handle this event
 372     NSInputManager *inputManager = [NSInputManager currentInputManager];
 373     if ([inputManager wantsToHandleMouseEvents]) {
 374         if ([inputManager handleMouseEvent:theEvent]) {
 375             return;
 376         }
 377     }
 378     [self->_delegate sendJavaMouseEvent:theEvent];
 379 }
 380 
 381 - (void)mouseDragged:(NSEvent *)theEvent
 382 {
 383     MOUSELOG("mouseDragged");
 384     [self->_delegate sendJavaMouseEvent:theEvent];
 385 }
 386 
 387 - (void)mouseUp:(NSEvent *)theEvent
 388 {
 389     MOUSELOG("mouseUp");
 390     [self->_delegate sendJavaMouseEvent:theEvent];
 391 }
 392 
 393 - (void)rightMouseDown:(NSEvent *)theEvent
 394 {
 395     MOUSELOG("rightMouseDown");
 396     [self->_delegate sendJavaMouseEvent:theEvent];
 397     // NOTE: menuForEvent: is invoked differently for right-click
 398     // and Ctrl+Click actions. So instead we always synthesize
 399     // the menu event in Glass. See sendJavaMouseEvent for details.
 400 }
 401 
 402 - (void)rightMouseDragged:(NSEvent *)theEvent
 403 {
 404     MOUSELOG("rightMouseDragged");
 405     [self->_delegate sendJavaMouseEvent:theEvent];
 406 }
 407 
 408 - (void)rightMouseUp:(NSEvent *)theEvent
 409 {
 410     MOUSELOG("rightMouseUp");
 411     [self->_delegate sendJavaMouseEvent:theEvent];
 412 }
 413 
 414 - (void)otherMouseDown:(NSEvent *)theEvent
 415 {
 416     MOUSELOG("otherMouseDown");
 417     [self->_delegate sendJavaMouseEvent:theEvent];
 418 }
 419 
 420 - (void)otherMouseDragged:(NSEvent *)theEvent
 421 {
 422     MOUSELOG("otherMouseDragged");
 423     [self->_delegate sendJavaMouseEvent:theEvent];
 424 }
 425 
 426 - (void)otherMouseUp:(NSEvent *)theEvent
 427 {
 428     MOUSELOG("otherMouseUp");
 429     [self->_delegate sendJavaMouseEvent:theEvent];
 430 }
 431 
 432 - (void)rotateWithEvent:(NSEvent *)theEvent
 433 {
 434     [self->_delegate sendJavaGestureEvent:theEvent type:com_sun_glass_ui_mac_MacGestureSupport_GESTURE_ROTATE];
 435 }
 436 
 437 - (void)swipeWithEvent:(NSEvent *)theEvent
 438 {
 439     [self->_delegate sendJavaGestureEvent:theEvent type:com_sun_glass_ui_mac_MacGestureSupport_GESTURE_SWIPE];
 440 }
 441 
 442 - (void)magnifyWithEvent:(NSEvent *)theEvent
 443 {
 444     [self->_delegate sendJavaGestureEvent:theEvent type:com_sun_glass_ui_mac_MacGestureSupport_GESTURE_MAGNIFY];
 445 }
 446 
 447 - (void)endGestureWithEvent:(NSEvent *)theEvent
 448 {
 449     [self->_delegate sendJavaGestureEndEvent:theEvent];
 450 }
 451 
 452 - (void)beginGestureWithEvent:(NSEvent *)theEvent
 453 {
 454     [self->_delegate sendJavaGestureBeginEvent:theEvent];
 455 }
 456 
 457 - (void)scrollWheel:(NSEvent *)theEvent
 458 {
 459     MOUSELOG("scrollWheel");
 460     [self->_delegate sendJavaMouseEvent:theEvent];
 461 }
 462 
 463 - (BOOL)performKeyEquivalent:(NSEvent *)theEvent
 464 {
 465     KEYLOG("performKeyEquivalent");
 466     [GlassApplication registerKeyEvent:theEvent];
 467 
 468     // Crash if the FS window is released while performing a key equivalent
 469     // Local copy of the id keeps the retain/release calls balanced.
 470     id fsWindow = [self->_delegate->fullscreenWindow retain];
 471 
 472     // RT-37093, RT-37399 Command-EQUALS and Command-DOT needs special casing on Mac
 473     // as it is passed through as two calls to performKeyEquivalent, which in turn
 474     // create extra KeyEvents.
 475     //
 476     NSString *chars = [theEvent charactersIgnoringModifiers];
 477     if ([theEvent type] == NSKeyDown && [chars length] > 0)
 478     {
 479         unichar uch = [chars characterAtIndex:0];
 480         if ([theEvent modifierFlags] & NSCommandKeyMask &&
 481             (uch == com_sun_glass_events_KeyEvent_VK_PERIOD ||
 482              uch == com_sun_glass_events_KeyEvent_VK_EQUALS))
 483         {
 484             GET_MAIN_JENV;
 485 
 486             jcharArray jKeyChars = GetJavaKeyChars(env, theEvent);
 487             jint jModifiers = GetJavaModifiers(theEvent);
 488 
 489             (*env)->CallVoidMethod(env, self->_delegate->jView, jViewNotifyKey,
 490                                    com_sun_glass_events_KeyEvent_PRESS,
 491                                    uch, jKeyChars, jModifiers);
 492             (*env)->CallVoidMethod(env, self->_delegate->jView, jViewNotifyKey,
 493                                    com_sun_glass_events_KeyEvent_TYPED,
 494                                    uch, jKeyChars, jModifiers);
 495             (*env)->CallVoidMethod(env, self->_delegate->jView, jViewNotifyKey,
 496                                    com_sun_glass_events_KeyEvent_RELEASE,
 497                                    uch, jKeyChars, jModifiers);
 498             (*env)->DeleteLocalRef(env, jKeyChars);
 499 
 500             GLASS_CHECK_EXCEPTION(env);
 501             [fsWindow release];
 502             return YES;
 503         }
 504     }
 505     [self->_delegate sendJavaKeyEvent:theEvent isDown:YES];
 506     [fsWindow release];
 507     return NO; // return NO to allow system-default processing of Cmd+Q, etc.
 508 }
 509 
 510 - (void)keyDown:(NSEvent *)theEvent
 511 {
 512     KEYLOG("keyDown");
 513     [GlassApplication registerKeyEvent:theEvent];
 514 
 515     if (![[self inputContext] handleEvent:theEvent] || shouldProcessKeyEvent) {
 516         [self->_delegate sendJavaKeyEvent:theEvent isDown:YES];
 517     }
 518     shouldProcessKeyEvent = YES;
 519 }
 520 
 521 - (void)keyUp:(NSEvent *)theEvent
 522 {
 523     KEYLOG("keyUp");
 524     [self->_delegate sendJavaKeyEvent:theEvent isDown:NO];
 525 }
 526 
 527 - (void)flagsChanged:(NSEvent *)theEvent
 528 {
 529     KEYLOG("flagsChanged");
 530     [self->_delegate sendJavaModifierKeyEvent:theEvent];
 531 }
 532 
 533 - (BOOL)wantsPeriodicDraggingUpdates
 534 {
 535     // we only want want updated drag operations when the mouse position changes
 536     return NO;
 537 }
 538 
 539 - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender
 540 {
 541     DNDLOG("prepareForDragOperation");
 542     return YES;
 543 }
 544 
 545 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
 546 {
 547     DNDLOG("performDragOperation");
 548     [self->_delegate sendJavaDndEvent:sender type:com_sun_glass_events_DndEvent_PERFORM];
 549 
 550     return YES;
 551 }
 552 
 553 - (void)concludeDragOperation:(id <NSDraggingInfo>)sender
 554 {
 555     DNDLOG("concludeDragOperation");
 556 }
 557 
 558 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
 559 {
 560     DNDLOG("draggingEntered");
 561     return [self->_delegate sendJavaDndEvent:sender type:com_sun_glass_events_DndEvent_ENTER];
 562 }
 563 
 564 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
 565 {
 566     DNDLOG("draggingUpdated");
 567     return [self->_delegate sendJavaDndEvent:sender type:com_sun_glass_events_DndEvent_UPDATE];
 568 }
 569 
 570 - (void)draggingEnded:(id <NSDraggingInfo>)sender
 571 {
 572     DNDLOG("draggingEnded");
 573     [self->_delegate draggingEnded];
 574 }
 575 
 576 - (void)draggingExited:(id <NSDraggingInfo>)sender
 577 {
 578     DNDLOG("draggingExited");
 579     [self->_delegate sendJavaDndEvent:sender type:com_sun_glass_events_DndEvent_EXIT];
 580 }
 581 
 582 - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
 583 {
 584     // Deprecated for 10.7
 585     // use NSDraggingSession - (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context
 586     DNDLOG("draggingSourceOperationMaskForLocal");
 587     return [self->_delegate draggingSourceOperationMaskForLocal:isLocal];
 588 }
 589 
 590 #pragma mark --- Callbacks
 591 
 592 - (void)enterFullscreenWithAnimate:(BOOL)animate withKeepRatio:(BOOL)keepRatio withHideCursor:(BOOL)hideCursor
 593 {
 594     [self->_delegate enterFullscreenWithAnimate:animate withKeepRatio:keepRatio withHideCursor:hideCursor];
 595 }
 596 
 597 - (void)exitFullscreenWithAnimate:(BOOL)animate
 598 {
 599     [self->_delegate exitFullscreenWithAnimate:animate];
 600 }
 601 
 602 - (void)begin
 603 {
 604     LOG("begin");
 605     assert(self->_drawCounter >= 0);
 606 
 607     if (self->_drawCounter == 0)
 608     {
 609         GlassLayer3D *layer = (GlassLayer3D*)[self layer];
 610         NSRect bounds = (self->isHiDPIAware && [self respondsToSelector:@selector(convertRectToBacking:)]) ?
 611             [self convertRectToBacking:[self bounds]] : [self bounds];
 612         [[layer getPainterOffscreen] bindForWidth:(GLuint)bounds.size.width andHeight:(GLuint)bounds.size.height];
 613     }
 614     self->_drawCounter++;
 615 }
 616 
 617 - (void)end
 618 {
 619     assert(self->_drawCounter > 0);
 620 
 621     self->_drawCounter--;
 622     if (self->_drawCounter == 0)
 623     {
 624         GlassLayer3D *layer = (GlassLayer3D*)[self layer];
 625         [[layer getPainterOffscreen] unbind];
 626         [layer flush];
 627     }
 628     LOG("end");
 629 }
 630 
 631 - (void)drawRect:(NSRect)dirtyRect
 632 {
 633     [self->_delegate drawRect:dirtyRect];
 634 }
 635 
 636 - (void)pushPixels:(void*)pixels withWidth:(GLuint)width withHeight:(GLuint)height withScale:(GLfloat)scale withEnv:(JNIEnv *)env
 637 {
 638     assert(self->_drawCounter > 0);
 639 
 640     if (self->_texture == 0)
 641     {
 642         glGenTextures(1, &self->_texture);
 643     }
 644 
 645     BOOL uploaded = NO;
 646     if ((self->_textureWidth != width) || (self->_textureHeight != height))
 647     {
 648         uploaded = YES;
 649 
 650         self->_textureWidth = width;
 651         self->_textureHeight = height;
 652 
 653         // GL_EXT_texture_rectangle is defined in OS X 10.6 GL headers, so we can depend on GL_TEXTURE_RECTANGLE_EXT being available
 654         glBindTexture(GL_TEXTURE_RECTANGLE_EXT, self->_texture);
 655         glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 656         glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 657         glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP);
 658         glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP);
 659         glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA8, (GLsizei)self->_textureWidth, (GLsizei)self->_textureHeight, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pixels);
 660     }
 661 
 662     glEnable(GL_TEXTURE_RECTANGLE_EXT);
 663     glBindTexture(GL_TEXTURE_RECTANGLE_EXT, self->_texture);
 664     {
 665         if (uploaded == NO)
 666         {
 667             glTexSubImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, 0, 0, (GLsizei)self->_textureWidth, (GLsizei)self->_textureHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pixels);
 668         }
 669 
 670         GLfloat w = self->_textureWidth;
 671         GLfloat h = self->_textureHeight;
 672 
 673         NSSize size = [self bounds].size;
 674         size.width *= scale;
 675         size.height *= scale;
 676         if ((size.width != w) || (size.height != h))
 677         {
 678             // This could happen on live resize, clear the FBO to avoid rendering garbage
 679             glClear(GL_COLOR_BUFFER_BIT);
 680         }
 681 
 682         glMatrixMode(GL_PROJECTION);
 683         glPushMatrix();
 684         glLoadIdentity();
 685         glOrtho(0.0f, size.width, size.height, 0.0f, -1.0f, 1.0f);
 686         {
 687             glMatrixMode(GL_MODELVIEW);
 688             glPushMatrix();
 689             glLoadIdentity();
 690             {
 691                 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); // copy
 692 
 693                 glBegin(GL_QUADS);
 694                 {
 695                     glTexCoord2f(0.0f, 0.0f); glVertex2f(0.0f, 0.0f);
 696                     glTexCoord2f(   w, 0.0f); glVertex2f(   w, 0.0f);
 697                     glTexCoord2f(   w,    h); glVertex2f(   w,    h);
 698                     glTexCoord2f(0.0f,    h); glVertex2f(0.0f,    h);
 699                 }
 700                 glEnd();
 701             }
 702             glMatrixMode(GL_MODELVIEW);
 703             glPopMatrix();
 704         }
 705         glMatrixMode(GL_PROJECTION);
 706         glPopMatrix();
 707     }
 708     glBindTexture(GL_TEXTURE_RECTANGLE_EXT, 0);
 709     glDisable(GL_TEXTURE_RECTANGLE_EXT);
 710 
 711     glFinish();
 712 
 713     // The layer will be notified about redraw in _end()
 714 }
 715 
 716 - (GlassViewDelegate*)delegate
 717 {
 718     return self->_delegate;
 719 }
 720 
 721 - (void)setInputMethodEnabled:(BOOL)enabled
 722 {
 723     IMLOG("setInputMethodEnabled called with arg is %s", (enabled ? "YES" : "NO") );
 724     [self unmarkText];
 725     self->imEnabled = enabled;
 726 }
 727 
 728 /*
 729  NSTextInputClient protocol implementation follows here.
 730  */
 731 
 732 - (void)doCommandBySelector:(SEL)aSelector
 733 {
 734     IMLOG("doCommandBySelector called ");
 735     // In case the IM was stopped with a mouse and the next typed key
 736     // is a special command key (backspace, tab, etc.)
 737     self->shouldProcessKeyEvent = YES;
 738 }
 739 
 740 - (void) insertText:(id)aString replacementRange:(NSRange)replacementRange
 741 {
 742     IMLOG("insertText called with string: %s", [aString UTF8String]);
 743     if ([self->nsAttrBuffer length] > 0 || [aString length] > 1) {
 744         [self->_delegate notifyInputMethod:aString attr:4 length:(int)[aString length] cursor:(int)[aString length] selectedRange: NSMakeRange(NSNotFound, 0)];
 745         self->shouldProcessKeyEvent = NO;
 746     } else {
 747         self->shouldProcessKeyEvent = YES;
 748     }
 749     self->nsAttrBuffer = [self->nsAttrBuffer initWithString:@""];
 750 }
 751 
 752 - (void) setMarkedText:(id)aString selectedRange:(NSRange)selectionRange replacementRange:(NSRange)replacementRange
 753 {
 754     if (!self->imEnabled) {
 755         self->shouldProcessKeyEvent = YES;
 756         return;
 757     }
 758     BOOL isAttributedString = [aString isKindOfClass:[NSAttributedString class]];
 759     NSAttributedString *attrString = (isAttributedString ? (NSAttributedString *)aString : nil);
 760     NSString *incomingString = (isAttributedString ? [aString string] : aString);
 761     IMLOG("setMarkedText called, attempt to set string to %s", [incomingString UTF8String]);
 762     [self->_delegate notifyInputMethod:incomingString attr:1 length:0 cursor:(int)[incomingString length] selectedRange:selectionRange ];
 763     self->nsAttrBuffer = (attrString == nil ? [self->nsAttrBuffer initWithString:incomingString]
 764                                             : [self->nsAttrBuffer initWithAttributedString: attrString]);
 765     self->shouldProcessKeyEvent = NO;
 766 }
 767 
 768 - (void) unmarkText
 769 {
 770     IMLOG("unmarkText called\n");
 771     if (self->nsAttrBuffer != nil && self->nsAttrBuffer.length != 0) {
 772         self->nsAttrBuffer = [self->nsAttrBuffer initWithString:@""];
 773         [self->_delegate notifyInputMethod:@"" attr:4 length:0 cursor:0 selectedRange: NSMakeRange(NSNotFound, 0)];
 774     }
 775     self->shouldProcessKeyEvent = YES;
 776 }
 777 
 778 - (BOOL) hasMarkedText
 779 {
 780     BOOL hmText = (self->imEnabled ? (self->nsAttrBuffer.length == 0 ? FALSE : TRUE) : FALSE);
 781     IMLOG("hasMarkedText called return %s ", (hmText ? "true" : "false"));
 782     return hmText;
 783 }
 784 
 785 - (NSRange) markedRange
 786 {
 787     IMLOG("markedRange called, return range in attributed string");
 788     if (self->imEnabled) {
 789         return NSMakeRange(0, self->nsAttrBuffer.length);
 790     } else {
 791         return NSMakeRange(NSNotFound, 0);
 792     }
 793 }
 794 
 795 - (NSAttributedString *) attributedSubstringForProposedRange:(NSRange)theRange actualRange:(NSRangePointer)actualRange
 796 {
 797     IMLOG("attributedSubstringFromRange called: location=%lu, length=%lu",
 798             (unsigned long)theRange.location, (unsigned long)theRange.length);
 799     if (self->imEnabled) {
 800         return self->nsAttrBuffer;
 801     } else {
 802         return NULL;
 803     }
 804 }
 805 
 806 - (NSRange) selectedRange
 807 {
 808     IMLOG("selectedRange called");
 809     if (self->imEnabled) {
 810         return NSMakeRange(0, [[self->nsAttrBuffer string] length]);
 811     } else {
 812         return NSMakeRange(NSNotFound, 0);
 813     }
 814 }
 815 
 816 - (NSRect) firstRectForCharacterRange:(NSRange)theRange actualRange:(NSRangePointer)actualRange
 817 {
 818     IMLOG("firstRectForCharacterRange called %lu %lu",
 819           (unsigned long)theRange.location, (unsigned long)theRange.length);
 820     NSRect result = [self->_delegate getInputMethodCandidatePosRequest:0];
 821     NSRect screenFrame = [[NSScreen mainScreen] frame];
 822     result.origin.y = screenFrame.size.height - result.origin.y;
 823     return result;
 824 }
 825 
 826 - (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
 827 {
 828     IMLOG("characterIndexForPoint (%f, %f) called", thePoint.x, thePoint.y);
 829     return 0;
 830 }
 831 
 832 - (NSArray*) validAttributesForMarkedText
 833 {
 834     return [NSArray array];
 835 }
 836 
 837 - (void)notifyScaleFactorChanged:(CGFloat)scale
 838 {
 839     GlassLayer3D *layer = (GlassLayer3D*)[self layer];
 840     [layer notifyScaleFactorChanged:scale];
 841 }
 842 
 843 /* Accessibility support */
 844 
 845 - (NSArray *)accessibilityAttributeNames
 846 {
 847     NSArray *names = NULL;
 848     GlassAccessible *accessible = [self->_delegate getAccessible];
 849     if (accessible) {
 850         names = [accessible accessibilityAttributeNames];
 851     }
 852     if (names == NULL) {
 853         names = [super accessibilityAttributeNames];
 854     }
 855     return names;
 856 }
 857 
 858 - (id)accessibilityAttributeValue:(NSString *)attribute
 859 {
 860     id value = NULL;
 861     GlassAccessible *accessible = [self->_delegate getAccessible];
 862     if (accessible) {
 863         value = [accessible accessibilityAttributeValue: attribute];
 864     }
 865     if (value == NULL) {
 866         value = [super accessibilityAttributeValue: attribute];
 867     }
 868     return value;
 869 }
 870 
 871 - (BOOL)accessibilityIsIgnored
 872 {
 873     BOOL value = TRUE; /* This default value in the OS */
 874     GlassAccessible *accessible = [self->_delegate getAccessible];
 875     if (accessible) {
 876         value = [accessible accessibilityIsIgnored];
 877     }
 878     return value;
 879 }
 880 
 881 - (id)accessibilityHitTest:(NSPoint)point
 882 {
 883     id value = NULL;
 884     GlassAccessible *accessible = [self->_delegate getAccessible];
 885     if (accessible) {
 886         value = [accessible accessibilityHitTest: point];
 887     }
 888     if (value == NULL) {
 889         value = [super accessibilityHitTest: point];
 890     }
 891     return value;
 892 }
 893 
 894 - (id)accessibilityFocusedUIElement
 895 {
 896     id value = NULL;
 897     GlassAccessible *accessible = [self->_delegate getAccessible];
 898     if (accessible) {
 899         value = [accessible accessibilityFocusedUIElement];
 900     }
 901     if (value == NULL) {
 902         value = [super accessibilityFocusedUIElement];
 903     }
 904     return value;
 905 }
 906 
 907 
 908 @end