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