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