1 /* 2 * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. 3 * Copyright (c) 2010 JogAmp Community. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * - Redistribution of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * - Redistribution in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * Neither the name of Sun Microsystems, Inc. or the names of 17 * contributors may be used to endorse or promote products derived from 18 * this software without specific prior written permission. 19 * 20 * This software is provided "AS IS," without a warranty of any kind. ALL 21 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, 22 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A 23 * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN 24 * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR 25 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR 26 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR 27 * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR 28 * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE 29 * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, 30 * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF 31 * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 32 * 33 */ 34 35 package jogamp.newt.driver.macosx; 36 37 import com.jogamp.common.util.InterruptSource; 38 import com.jogamp.nativewindow.AbstractGraphicsConfiguration; 39 import com.jogamp.nativewindow.GraphicsConfigurationFactory; 40 import com.jogamp.nativewindow.NativeWindow; 41 import com.jogamp.nativewindow.NativeWindowException; 42 import com.jogamp.nativewindow.MutableSurface; 43 import com.jogamp.nativewindow.ScalableSurface; 44 import com.jogamp.nativewindow.VisualIDHolder; 45 import com.jogamp.nativewindow.util.Point; 46 import com.jogamp.nativewindow.util.PointImmutable; 47 48 import jogamp.nativewindow.SurfaceScaleUtils; 49 import jogamp.nativewindow.macosx.OSXUtil; 50 import jogamp.newt.PointerIconImpl; 51 import jogamp.newt.ScreenImpl; 52 import jogamp.newt.WindowImpl; 53 import jogamp.newt.driver.DriverClearFocus; 54 import jogamp.newt.driver.DriverUpdatePosition; 55 56 import com.jogamp.newt.event.InputEvent; 57 import com.jogamp.newt.event.KeyEvent; 58 import com.jogamp.newt.event.MonitorEvent; 59 import com.jogamp.opengl.math.FloatUtil; 60 61 public class WindowDriver extends WindowImpl implements MutableSurface, DriverClearFocus, DriverUpdatePosition { 62 63 static { DisplayDriver.initSingleton()64 DisplayDriver.initSingleton(); 65 } 66 WindowDriver()67 public WindowDriver() { 68 } 69 updatePixelScale(final boolean sendEvent, final boolean defer, final boolean deferOffThread, final float newPixelScaleRaw, final float maxPixelScaleRaw)70 private boolean updatePixelScale(final boolean sendEvent, final boolean defer, final boolean deferOffThread, 71 final float newPixelScaleRaw, final float maxPixelScaleRaw) { 72 final float[] newPixelScale = new float[2]; 73 { 74 final float _newPixelScale = FloatUtil.isZero(newPixelScaleRaw, FloatUtil.EPSILON) ? ScalableSurface.IDENTITY_PIXELSCALE : newPixelScaleRaw; 75 newPixelScale[0]= _newPixelScale; 76 newPixelScale[1]= _newPixelScale; 77 final float _maxPixelScale = FloatUtil.isZero(maxPixelScaleRaw, FloatUtil.EPSILON) ? ScalableSurface.IDENTITY_PIXELSCALE : maxPixelScaleRaw; 78 maxPixelScale[0]= _maxPixelScale; 79 maxPixelScale[1]= _maxPixelScale; 80 } 81 // We keep minPixelScale at [1f, 1f]! 82 83 if( SurfaceScaleUtils.setNewPixelScale(hasPixelScale, hasPixelScale, newPixelScale, minPixelScale, maxPixelScale, DEBUG_IMPLEMENTATION ? getClass().getName() : null) ) { 84 if( sendEvent ) { 85 if( deferOffThread ) { 86 superSizeChangedOffThread(defer, getWidth(), getHeight(), true); 87 } else { 88 super.sizeChanged(defer, getWidth(), getHeight(), true); 89 } 90 } else { 91 defineSize(getWidth(), getHeight()); 92 } 93 return true; 94 } else { 95 return false; 96 } 97 } 98 updatePixelScaleByDisplayID(final boolean sendEvent)99 private boolean updatePixelScaleByDisplayID(final boolean sendEvent) { 100 final float maxPixelScaleRaw = (float) OSXUtil.GetPixelScaleByDisplayID(getDisplayID()); 101 if( DEBUG_IMPLEMENTATION ) { 102 System.err.println("WindowDriver.updatePixelScale.1: "+hasPixelScale[0]+", "+maxPixelScaleRaw+" (max)"); 103 } 104 return updatePixelScale(sendEvent, true /* defer */, false /*offthread */, maxPixelScaleRaw, maxPixelScaleRaw); 105 } 106 updatePixelScaleByWindowHandle(final boolean sendEvent)107 private boolean updatePixelScaleByWindowHandle(final boolean sendEvent) { 108 final long handle = getWindowHandle(); 109 if( 0 != handle ) { 110 final float maxPixelScaleRaw = (float)OSXUtil.GetPixelScale(handle); 111 if( DEBUG_IMPLEMENTATION ) { 112 System.err.println("WindowDriver.updatePixelScale.2: "+hasPixelScale[0]+", "+maxPixelScaleRaw+" (max)"); 113 } 114 return updatePixelScale(sendEvent, true /* defer */, false /*offthread */, maxPixelScaleRaw, maxPixelScaleRaw); 115 } else { 116 return false; 117 } 118 } 119 120 /** Called from native code */ updatePixelScale(final boolean defer, final float newPixelScaleRaw, final float maxPixelScaleRaw)121 protected void updatePixelScale(final boolean defer, final float newPixelScaleRaw, final float maxPixelScaleRaw) { 122 if( DEBUG_IMPLEMENTATION ) { 123 System.err.println("WindowDriver.updatePixelScale.3: "+hasPixelScale[0]+" (has) -> "+newPixelScaleRaw+" (new), "+maxPixelScaleRaw+" (max), drop "+!isNativeValid()); 124 } 125 if( isNativeValid() ) { 126 updatePixelScale(true /* sendEvent*/, defer, true /*offthread */, newPixelScaleRaw, maxPixelScaleRaw); 127 } 128 } 129 130 @Override instantiationFinishedImpl()131 protected final void instantiationFinishedImpl() { 132 updatePixelScaleByDisplayID(false /* sendEvent*/); 133 } 134 135 @Override setScreen(final ScreenImpl newScreen)136 protected void setScreen(final ScreenImpl newScreen) { // never null ! 137 super.setScreen(newScreen); 138 updatePixelScaleByDisplayID(false /* sendEvent*/); // caller (reparent, ..) will send reshape event 139 } 140 141 @Override monitorModeChanged(final MonitorEvent me, final boolean success)142 protected void monitorModeChanged(final MonitorEvent me, final boolean success) { 143 updatePixelScaleByWindowHandle(false /* sendEvent*/); // send reshape event itself 144 } 145 146 @Override setSurfaceScale(final float[] pixelScale)147 public final boolean setSurfaceScale(final float[] pixelScale) { 148 super.setSurfaceScale(pixelScale); 149 150 boolean changed = false; 151 if( isNativeValid() ) { 152 if( isOffscreenInstance ) { 153 final NativeWindow pWin = getParent(); 154 if( pWin instanceof ScalableSurface ) { 155 final ScalableSurface sSurf = (ScalableSurface)pWin; 156 sSurf.setSurfaceScale(reqPixelScale); 157 sSurf.getMaximumSurfaceScale(maxPixelScale); 158 sSurf.getMinimumSurfaceScale(minPixelScale); 159 final float[] pPixelScale = sSurf.getCurrentSurfaceScale(new float[2]); 160 changed = updatePixelScale(true /* sendEvent */, true /* defer */, true /*offthread */, pPixelScale[0], maxPixelScale[0]); // HiDPI: uniformPixelScale 161 } else { 162 // just notify updated pixelScale if offscreen 163 changed = updatePixelScale(true /* sendEvent */, true /* defer */, true /*offthread */, reqPixelScale[0], maxPixelScale[0]); // HiDPI: uniformPixelScale 164 } 165 } else { 166 // set pixelScale in native code, will issue an update updatePixelScale(..) 167 // hence we pre-query whether pixel-scale will change, without affecting current state 'hasPixelScale'! 168 final float[] _hasPixelScale = new float[2]; 169 System.arraycopy(hasPixelScale, 0, _hasPixelScale, 0, 2); 170 if( SurfaceScaleUtils.setNewPixelScale(_hasPixelScale, _hasPixelScale, reqPixelScale, minPixelScale, maxPixelScale, DEBUG_IMPLEMENTATION ? getClass().getName() : null) ) { 171 OSXUtil.RunOnMainThread(true, false, new Runnable() { 172 @Override 173 public void run() { 174 setPixelScale0(getWindowHandle(), surfaceHandle, _hasPixelScale[0]); // HiDPI: uniformPixelScale 175 } 176 } ); 177 changed = true; 178 } 179 } 180 } 181 if( DEBUG_IMPLEMENTATION ) { 182 System.err.println("WindowDriver.setPixelScale: min["+minPixelScale[0]+", "+minPixelScale[1]+"], max["+ 183 maxPixelScale[0]+", "+maxPixelScale[1]+"], req["+ 184 reqPixelScale[0]+", "+reqPixelScale[1]+"] -> result["+ 185 hasPixelScale[0]+", "+hasPixelScale[1]+"] - changed "+changed+", realized "+isNativeValid()); 186 } 187 return changed; 188 } 189 190 @Override createNativeImpl()191 protected void createNativeImpl() { 192 final AbstractGraphicsConfiguration cfg = GraphicsConfigurationFactory.getFactory(getScreen().getDisplay().getGraphicsDevice(), capsRequested).chooseGraphicsConfiguration( 193 capsRequested, capsRequested, capabilitiesChooser, getScreen().getGraphicsScreen(), VisualIDHolder.VID_UNDEFINED); 194 if (null == cfg) { 195 throw new NativeWindowException("Error choosing GraphicsConfiguration creating window: "+this); 196 } 197 setGraphicsConfiguration(cfg); 198 reconfigureWindowImpl(getX(), getY(), getWidth(), getHeight(), getReconfigureMask(CHANGE_MASK_VISIBILITY, true)); 199 if ( !isNativeValid() ) { 200 throw new NativeWindowException("Error creating window"); 201 } 202 } 203 204 @Override closeNativeImpl()205 protected void closeNativeImpl() { 206 try { 207 if(DEBUG_IMPLEMENTATION) { System.err.println("MacWindow.CloseAction "+Thread.currentThread().getName()); } 208 final long handle = getWindowHandle(); 209 visibleChanged(true, false); 210 setWindowHandle(0); 211 surfaceHandle = 0; 212 sscSurfaceHandle = 0; 213 isOffscreenInstance = false; 214 if (0 != handle) { 215 OSXUtil.RunOnMainThread(false, true /* kickNSApp */, new Runnable() { 216 @Override 217 public void run() { 218 close0( handle ); 219 } }); 220 } 221 } catch (final Throwable t) { 222 if(DEBUG_IMPLEMENTATION) { 223 final Exception e = new Exception("Warning: closeNative failed - "+Thread.currentThread().getName(), t); 224 e.printStackTrace(); 225 } 226 } 227 } 228 229 @Override lockSurfaceImpl()230 protected int lockSurfaceImpl() { 231 /** 232 * if( isOffscreenInstance ) { 233 * return LOCK_SUCCESS; 234 * } 235 */ 236 final long w = getWindowHandle(); 237 final long v = surfaceHandle; 238 if( 0 != v && 0 != w ) { 239 return lockSurface0(w, v) ? LOCK_SUCCESS : LOCK_SURFACE_NOT_READY; 240 } 241 return LOCK_SURFACE_NOT_READY; 242 } 243 244 @Override unlockSurfaceImpl()245 protected void unlockSurfaceImpl() { 246 /** 247 * if( isOffscreenInstance ) { 248 * return; 249 * } 250 */ 251 final long w = getWindowHandle(); 252 final long v = surfaceHandle; 253 if(0 != w && 0 != v) { 254 if( !unlockSurface0(w, v) ) { 255 throw new NativeWindowException("Failed to unlock surface, probably not locked!"); 256 } 257 } 258 } 259 260 @Override getSurfaceHandle()261 public final long getSurfaceHandle() { 262 return 0 != sscSurfaceHandle ? sscSurfaceHandle : surfaceHandle; 263 } 264 265 @Override setSurfaceHandle(final long surfaceHandle)266 public void setSurfaceHandle(final long surfaceHandle) { 267 if(DEBUG_IMPLEMENTATION) { 268 System.err.println("MacWindow.setSurfaceHandle(): 0x"+Long.toHexString(surfaceHandle)); 269 } 270 sscSurfaceHandle = surfaceHandle; 271 if ( isNativeValid() ) { 272 if (0 != sscSurfaceHandle) { 273 OSXUtil.RunOnMainThread(false, false, new Runnable() { 274 @Override 275 public void run() { 276 orderOut0( 0 != getParentWindowHandle() ? getParentWindowHandle() : getWindowHandle() ); 277 } } ); 278 } /** this is done by recreation! 279 else if (isVisible()){ 280 OSXUtil.RunOnMainThread(false, new Runnable() { 281 public void run() { 282 orderFront0( 0!=getParentWindowHandle() ? getParentWindowHandle() : getWindowHandle() ); 283 } } ); 284 } */ 285 } 286 } 287 288 @Override setTitleImpl(final String title)289 protected void setTitleImpl(final String title) { 290 OSXUtil.RunOnMainThread(false, false, new Runnable() { 291 @Override 292 public void run() { 293 setTitle0(getWindowHandle(), title); 294 } } ); 295 } 296 297 @Override requestFocusImpl(final boolean force)298 protected void requestFocusImpl(final boolean force) { 299 final boolean _isFullscreen = isFullscreen(); 300 final boolean _isOffscreenInstance = isOffscreenInstance; 301 if(DEBUG_IMPLEMENTATION) { 302 System.err.println("MacWindow: requestFocusImpl(), isOffscreenInstance "+_isOffscreenInstance+", isFullscreen "+_isFullscreen); 303 } 304 if(!_isOffscreenInstance) { 305 OSXUtil.RunOnMainThread(false, false, new Runnable() { 306 @Override 307 public void run() { 308 requestFocus0(getWindowHandle(), force); 309 if(_isFullscreen) { 310 // 'NewtMacWindow::windowDidBecomeKey()' is not always called in fullscreen-mode! 311 focusChanged(false, true); 312 } 313 } } ); 314 } else { 315 focusChanged(false, true); 316 } 317 } 318 319 @Override clearFocus()320 public final void clearFocus() { 321 if(DEBUG_IMPLEMENTATION) { 322 System.err.println("MacWindow: clearFocus(), isOffscreenInstance "+isOffscreenInstance); 323 } 324 if(!isOffscreenInstance) { 325 OSXUtil.RunOnMainThread(false, false, new Runnable() { 326 @Override 327 public void run() { 328 resignFocus0(getWindowHandle()); 329 } } ); 330 } else { 331 focusChanged(false, false); 332 } 333 } 334 useParent(final NativeWindow parent)335 private boolean useParent(final NativeWindow parent) { return null != parent && 0 != parent.getWindowHandle(); } 336 337 @Override updatePosition(final int x, final int y)338 public void updatePosition(final int x, final int y) { 339 final long handle = getWindowHandle(); 340 if( 0 != handle && !isOffscreenInstance ) { 341 final NativeWindow parent = getParent(); 342 final boolean useParent = useParent(parent); 343 final Point p0S; 344 if( useParent ) { 345 p0S = getLocationOnScreenByParent(x, y, parent); 346 } else { 347 p0S = (Point) getLocationOnScreen0(handle, x, y); 348 } 349 if(DEBUG_IMPLEMENTATION) { 350 final int pX=parent.getX(), pY=parent.getY(); 351 System.err.println("MacWindow: updatePosition() parent["+useParent+" "+pX+"/"+pY+"] "+x+"/"+y+" -> "+x+"/"+y+" rel-client-pos, "+p0S+" screen-client-pos"); 352 } 353 OSXUtil.RunOnMainThread(false, false, new Runnable() { 354 @Override 355 public void run() { 356 setWindowClientTopLeftPoint0(getWindowHandle(), p0S.getX(), p0S.getY(), isVisible()); 357 } } ); 358 // no native event (fullscreen, some reparenting) 359 positionChanged(true, x, y); 360 } 361 } 362 363 @Override getSupportedReconfigMaskImpl()364 protected final int getSupportedReconfigMaskImpl() { 365 return minimumReconfigStateMask | 366 STATE_MASK_CHILDWIN | 367 STATE_MASK_UNDECORATED | 368 STATE_MASK_ALWAYSONTOP | 369 STATE_MASK_ALWAYSONBOTTOM | 370 STATE_MASK_STICKY | 371 STATE_MASK_RESIZABLE | 372 STATE_MASK_MAXIMIZED_VERT | 373 STATE_MASK_MAXIMIZED_HORZ | 374 STATE_MASK_POINTERVISIBLE | 375 STATE_MASK_POINTERCONFINED; 376 } 377 378 @Override reconfigureWindowImpl(int _x, int _y, int _width, int _height, final int flags)379 protected boolean reconfigureWindowImpl(int _x, int _y, int _width, int _height, final int flags) { 380 final boolean _isOffscreenInstance = isOffscreenInstance(this, this.getParent()); 381 isOffscreenInstance = 0 != sscSurfaceHandle || _isOffscreenInstance; 382 final PointImmutable pClientLevelOnSreen; 383 if( isOffscreenInstance ) { 384 _x = 0; _y = 0; 385 pClientLevelOnSreen = new Point(0, 0); 386 } else { 387 final NativeWindow parent = getParent(); 388 if( useParent(parent) ) { 389 pClientLevelOnSreen = getLocationOnScreenByParent(_x, _y, parent); 390 } else { 391 if( 0 != ( ( CHANGE_MASK_MAXIMIZED_HORZ | CHANGE_MASK_MAXIMIZED_VERT ) & flags ) ) { 392 final int[] posSize = { _x, _y, _width, _height }; 393 reconfigMaximizedManual(flags, posSize, getInsets()); 394 _x = posSize[0]; 395 _y = posSize[1]; 396 _width = posSize[2]; 397 _height = posSize[3]; 398 } 399 pClientLevelOnSreen = new Point(_x, _y); 400 } 401 } 402 final int x=_x, y=_y; 403 final int width=_width, height=_height; 404 405 final boolean hasFocus = hasFocus(); 406 407 if(DEBUG_IMPLEMENTATION) { 408 final AbstractGraphicsConfiguration cWinCfg = this.getGraphicsConfiguration(); 409 final NativeWindow pWin = getParent(); 410 final AbstractGraphicsConfiguration pWinCfg = null != pWin ? pWin.getGraphicsConfiguration() : null; 411 System.err.println("MacWindow reconfig.0: "+x+"/"+y+" -> clientPosOnScreen "+pClientLevelOnSreen+" - "+width+"x"+height+ 412 ", "+getReconfigStateMaskString(flags)+ 413 ",\n\t parent type "+(null != pWin ? pWin.getClass().getName() : null)+ 414 ",\n\t this-chosenCaps "+(null != cWinCfg ? cWinCfg.getChosenCapabilities() : null)+ 415 ",\n\t parent-chosenCaps "+(null != pWinCfg ? pWinCfg.getChosenCapabilities() : null)+ 416 ", isOffscreenInstance(sscSurfaceHandle "+toHexString(sscSurfaceHandle)+ 417 ", ioi: "+_isOffscreenInstance+ 418 ") -> "+isOffscreenInstance); 419 // Thread.dumpStack(); 420 } 421 422 if( 0 != ( CHANGE_MASK_VISIBILITY & flags) && 423 0 == ( STATE_MASK_VISIBLE & flags) ) 424 { 425 if ( !isOffscreenInstance ) { 426 OSXUtil.RunOnMainThread(false, false, new Runnable() { 427 @Override 428 public void run() { 429 orderOut0(getWindowHandle()); 430 visibleChanged(true, false); 431 } } ); 432 } else { 433 visibleChanged(true, false); 434 } 435 } 436 final long oldWindowHandle = getWindowHandle(); 437 if( ( 0 == oldWindowHandle && 0 != ( STATE_MASK_VISIBLE & flags) ) || 438 0 != ( CHANGE_MASK_PARENTING & flags) || 439 0 != ( CHANGE_MASK_DECORATION & flags) || 440 0 != ( CHANGE_MASK_ALWAYSONTOP & flags) || 441 0 != ( CHANGE_MASK_ALWAYSONBOTTOM & flags) || 442 0 != ( CHANGE_MASK_RESIZABLE & flags) || 443 0 != ( CHANGE_MASK_FULLSCREEN & flags) ) { 444 if(isOffscreenInstance) { 445 createWindow(true, 0 != oldWindowHandle, pClientLevelOnSreen, 64, 64, flags); 446 } else { 447 createWindow(false, 0 != oldWindowHandle, pClientLevelOnSreen, width, height, flags); 448 } 449 // no native event (fullscreen, some reparenting) 450 updatePixelScaleByWindowHandle(false /* sendEvent */); 451 if( isOffscreenInstance) { 452 super.sizeChanged(false, width, height, true); 453 positionChanged(false, x, y); 454 } else { 455 updateSizePosInsets0(getWindowHandle(), false); 456 } 457 visibleChanged(false, 0 != ( STATE_MASK_VISIBLE & flags)); 458 if( hasFocus ) { 459 requestFocusImpl(true); 460 } 461 } else if( 0 != oldWindowHandle ) { 462 if( width>0 && height>0 ) { 463 if( !isOffscreenInstance ) { 464 OSXUtil.RunOnMainThread(true, false, new Runnable() { 465 @Override 466 public void run() { 467 setWindowClientTopLeftPointAndSize0(oldWindowHandle, 468 pClientLevelOnSreen.getX(), pClientLevelOnSreen.getY(), 469 width, height, 0 != ( STATE_MASK_VISIBLE & flags)); 470 } } ); 471 updateSizePosInsets0(oldWindowHandle, false); 472 } else { // else offscreen size is realized via recreation 473 // no native event (fullscreen, some reparenting) 474 super.sizeChanged(false, width, height, false); 475 positionChanged(false, x, y); 476 } 477 } 478 if( 0 != ( CHANGE_MASK_VISIBILITY & flags) && 479 0 != ( STATE_MASK_VISIBLE & flags) ) 480 { 481 if( !isOffscreenInstance ) { 482 OSXUtil.RunOnMainThread(false, false, new Runnable() { 483 @Override 484 public void run() { 485 orderFront0(getWindowHandle()); 486 visibleChanged(true, true); 487 } } ); 488 } else { 489 visibleChanged(true, true); 490 } 491 } 492 } else { 493 throw new InternalError("Null windowHandle but no re-creation triggered, check visibility: "+getStateMaskString()); 494 } 495 if(DEBUG_IMPLEMENTATION) { 496 System.err.println("MacWindow reconfig.X: "+getLocationOnScreenImpl(0, 0)+" "+getWidth()+"x"+getHeight()+", insets "+getInsets()+", "+getStateMaskString()); 497 } 498 return true; 499 } 500 501 @Override getLocationOnScreenImpl(final int x, final int y)502 protected Point getLocationOnScreenImpl(final int x, final int y) { 503 final NativeWindow parent = getParent(); 504 if( useParent(parent) ) { 505 return getLocationOnScreenByParent(x, y, parent); 506 } else { 507 final long windowHandle = getWindowHandle(); 508 if( !isOffscreenInstance && 0 != windowHandle ) { 509 return (Point) getLocationOnScreen0(windowHandle, x, y); 510 } else { 511 return new Point(x, y); 512 } 513 } 514 } 515 getLocationOnScreenByParent(final int x, final int y, final NativeWindow parent)516 private Point getLocationOnScreenByParent(final int x, final int y, final NativeWindow parent) { 517 return new Point(x, y).translate( parent.getLocationOnScreen(null) ); 518 } 519 520 /** Callback for native screen position change event of the client area. */ screenPositionChanged(final boolean defer, final int newX, final int newY)521 protected void screenPositionChanged(final boolean defer, final int newX, final int newY) { 522 // passed coordinates are in screen position of the client area 523 if( isNativeValid() ) { 524 final NativeWindow parent = getParent(); 525 if( !useParent(parent) || isOffscreenInstance ) { 526 if(DEBUG_IMPLEMENTATION) { 527 System.err.println("MacWindow.positionChanged.0 (Screen Pos - TOP): ("+getThreadName()+"): (defer: "+defer+") "+getX()+"/"+getY()+" -> "+newX+"/"+newY); 528 } 529 positionChanged(defer, newX, newY); 530 } else { 531 final Runnable action = new Runnable() { 532 public void run() { 533 // screen position -> rel child window position 534 final Point absPos = new Point(newX, newY); 535 final Point parentOnScreen = parent.getLocationOnScreen(null); 536 absPos.translate( parentOnScreen.scale(-1, -1) ); 537 if(DEBUG_IMPLEMENTATION) { 538 System.err.println("MacWindow.positionChanged.1 (Screen Pos - CHILD): ("+getThreadName()+"): (defer: "+defer+") "+getX()+"/"+getY()+" -> absPos "+newX+"/"+newY+", parentOnScreen "+parentOnScreen+" -> "+absPos); 539 } 540 positionChanged(false, absPos.getX(), absPos.getY()); 541 } }; 542 if( defer ) { 543 new InterruptSource.Thread(null, action).start(); 544 } else { 545 action.run(); 546 } 547 548 } 549 } else if(DEBUG_IMPLEMENTATION) { 550 System.err.println("MacWindow.positionChanged.2 (Screen Pos - IGN): ("+getThreadName()+"): (defer: "+defer+") "+getX()+"/"+getY()+" -> "+newX+"/"+newY); 551 } 552 } 553 554 @Override sizeChanged(final boolean defer, final int newWidth, final int newHeight, final boolean force)555 protected void sizeChanged(final boolean defer, final int newWidth, final int newHeight, final boolean force) { 556 if(force || getWidth() != newWidth || getHeight() != newHeight) { 557 if( isNativeValid() && !isOffscreenInstance ) { 558 final NativeWindow parent = getParent(); 559 final boolean useParent = useParent(parent); 560 if( useParent ) { 561 final int x=getX(), y=getY(); 562 final Point p0S = getLocationOnScreenByParent(x, y, parent); 563 if(DEBUG_IMPLEMENTATION) { 564 System.err.println("MacWindow: sizeChanged() parent["+useParent+" "+x+"/"+y+"] "+getX()+"/"+getY()+" "+newWidth+"x"+newHeight+" -> "+p0S+" screen-client-pos"); 565 } 566 OSXUtil.RunOnMainThread(false, false, new Runnable() { 567 @Override 568 public void run() { 569 setWindowClientTopLeftPoint0(getWindowHandle(), p0S.getX(), p0S.getY(), isVisible()); 570 } } ); 571 } 572 } 573 superSizeChangedOffThread(defer, newWidth, newHeight, force); 574 } 575 } superSizeChangedOffThread(final boolean defer, final int newWidth, final int newHeight, final boolean force)576 private void superSizeChangedOffThread(final boolean defer, final int newWidth, final int newHeight, final boolean force) { 577 if( defer ) { 578 new InterruptSource.Thread() { 579 public void run() { 580 WindowDriver.super.sizeChanged(false /* defer */, newWidth, newHeight, force); 581 } }.start(); 582 } else { 583 WindowDriver.super.sizeChanged(false /* defer */, newWidth, newHeight, force); 584 } 585 } 586 587 // 588 // Accumulated actions 589 // 590 591 /** Triggered by implementation's WM events to update the client-area position, size and insets. */ sizeScreenPosInsetsChanged(final boolean defer, final int newX, final int newY, final int newWidth, final int newHeight, final int left, final int right, final int top, final int bottom, final boolean force)592 protected void sizeScreenPosInsetsChanged(final boolean defer, 593 final int newX, final int newY, 594 final int newWidth, final int newHeight, 595 final int left, final int right, final int top, final int bottom, 596 final boolean force) { 597 sizeChanged(defer, newWidth, newHeight, force); 598 screenPositionChanged(defer, newX, newY); 599 insetsChanged(defer, left, right, top, bottom); 600 } 601 602 @Override setPointerIconImpl(final PointerIconImpl pi)603 protected void setPointerIconImpl(final PointerIconImpl pi) { 604 if( !isOffscreenInstance ) { 605 final long piHandle = null != pi ? pi.validatedHandle() : 0; 606 OSXUtil.RunOnMainThread(true, false, new Runnable() { // waitUntildone due to PointerIconImpl's Lifecycle ! 607 @Override 608 public void run() { 609 setPointerIcon0(getWindowHandle(), piHandle); 610 } } ); 611 } 612 } 613 614 @Override setPointerVisibleImpl(final boolean pointerVisible)615 protected boolean setPointerVisibleImpl(final boolean pointerVisible) { 616 if( !isOffscreenInstance ) { 617 OSXUtil.RunOnMainThread(false, false, new Runnable() { 618 @Override 619 public void run() { 620 setPointerVisible0(getWindowHandle(), hasFocus(), pointerVisible); 621 } } ); 622 return true; 623 } 624 return false; 625 } 626 627 @Override confinePointerImpl(final boolean confine)628 protected boolean confinePointerImpl(final boolean confine) { 629 if( !isOffscreenInstance ) { 630 confinePointer0(getWindowHandle(), confine); 631 return true; 632 } // else may need offscreen solution ? FIXME 633 return false; 634 } 635 636 @Override warpPointerImpl(final int x, final int y)637 protected void warpPointerImpl(final int x, final int y) { 638 if( !isOffscreenInstance ) { 639 warpPointer0(getWindowHandle(), 640 SurfaceScaleUtils.scaleInv(x, getPixelScaleX()), 641 SurfaceScaleUtils.scaleInv(y, getPixelScaleY())); 642 } // else may need offscreen solution ? FIXME 643 } 644 645 @Override doMouseEvent(final boolean enqueue, final boolean wait, final short eventType, final int modifiers, final int x, final int y, final short button, final float[] rotationXYZ, final float rotationScale)646 protected final void doMouseEvent(final boolean enqueue, final boolean wait, final short eventType, final int modifiers, 647 final int x, final int y, final short button, final float[] rotationXYZ, final float rotationScale) { 648 super.doMouseEvent(enqueue, wait, eventType, modifiers, 649 SurfaceScaleUtils.scale(x, getPixelScaleX()), 650 SurfaceScaleUtils.scale(y, getPixelScaleY()), button, rotationXYZ, rotationScale); 651 } 652 653 @Override sendKeyEvent(final short eventType, final int modifiers, final short keyCode, final short keySym, final char keyChar)654 public final void sendKeyEvent(final short eventType, final int modifiers, final short keyCode, final short keySym, final char keyChar) { 655 throw new InternalError("XXX: Adapt Java Code to Native Code Changes"); 656 } 657 658 @Override enqueueKeyEvent(final boolean wait, final short eventType, final int modifiers, final short _keyCode, final short _keySym, final char keyChar)659 public final void enqueueKeyEvent(final boolean wait, final short eventType, final int modifiers, final short _keyCode, final short _keySym, final char keyChar) { 660 throw new InternalError("XXX: Adapt Java Code to Native Code Changes"); 661 } 662 enqueueKeyEvent(final boolean wait, final short eventType, int modifiers, final short _keyCode, final char keyChar, final char keySymChar)663 protected final void enqueueKeyEvent(final boolean wait, final short eventType, int modifiers, final short _keyCode, final char keyChar, final char keySymChar) { 664 // Note that we send the key char for the key code on this 665 // platform -- we do not get any useful key codes out of the system 666 final short keyCode = MacKeyUtil.validateKeyCode(_keyCode, keyChar); 667 final short keySym; 668 { 669 final short _keySym = KeyEvent.NULL_CHAR != keySymChar ? KeyEvent.utf16ToVKey(keySymChar) : KeyEvent.VK_UNDEFINED; 670 keySym = KeyEvent.VK_UNDEFINED != _keySym ? _keySym : keyCode; 671 } 672 /** 673 { 674 final boolean isModifierKeyCode = KeyEvent.isModifierKey(keyCode); 675 System.err.println("*** handleKeyEvent: event "+KeyEvent.getEventTypeString(eventType)+ 676 ", keyCode 0x"+Integer.toHexString(_keyCode)+" -> 0x"+Integer.toHexString(keyCode)+ 677 ", keySymChar '"+keySymChar+"', 0x"+Integer.toHexString(keySymChar)+" -> 0x"+Integer.toHexString(keySym)+ 678 ", mods "+toHexString(modifiers)+ 679 ", was: pressed "+isKeyPressed(keyCode)+", isModifierKeyCode "+isModifierKeyCode+ 680 ", nativeValid "+isNativeValid()+", isOffscreen "+isOffscreenInstance); 681 } */ 682 683 // OSX delivery order is PRESSED (t0), RELEASED (t1) and TYPED (t2) -> NEWT order: PRESSED (t0) and RELEASED (t1) 684 // Auto-Repeat: OSX delivers only PRESSED, inject auto-repeat RELEASE key _before_ PRESSED 685 switch(eventType) { 686 case KeyEvent.EVENT_KEY_RELEASED: 687 if( isKeyCodeTracked(keyCode) ) { 688 setKeyPressed(keyCode, false); 689 } 690 break; 691 case KeyEvent.EVENT_KEY_PRESSED: 692 if( isKeyCodeTracked(keyCode) ) { 693 if( setKeyPressed(keyCode, true) ) { 694 // key was already pressed 695 modifiers |= InputEvent.AUTOREPEAT_MASK; 696 super.enqueueKeyEvent(wait, KeyEvent.EVENT_KEY_RELEASED, modifiers, keyCode, keySym, keyChar); // RELEASED 697 } 698 } 699 break; 700 } 701 super.enqueueKeyEvent(wait, eventType, modifiers, keyCode, keySym, keyChar); 702 } 703 getDisplayID()704 protected int getDisplayID() { 705 if( !isOffscreenInstance ) { 706 return getDisplayID0(getWindowHandle()); 707 } 708 return 0; 709 } 710 711 //---------------------------------------------------------------------- 712 // Internals only 713 // 714 createWindow(final boolean offscreenInstance, final boolean recreate, final PointImmutable pS, final int width, final int height, final int flags)715 private void createWindow(final boolean offscreenInstance, final boolean recreate, 716 final PointImmutable pS, final int width, final int height, 717 final int flags) 718 { 719 final long parentWinHandle = getParentWindowHandle(); 720 final long preWinHandle = getWindowHandle(); 721 722 if(DEBUG_IMPLEMENTATION) { 723 System.err.println("MacWindow.createWindow on thread "+Thread.currentThread().getName()+ 724 ": offscreen "+offscreenInstance+", recreate "+recreate+ 725 ", pS "+pS+", "+width+"x"+height+", state "+getReconfigStateMaskString(flags)+ 726 ", preWinHandle "+toHexString(preWinHandle)+", parentWin "+toHexString(parentWinHandle)+ 727 ", surfaceHandle "+toHexString(surfaceHandle)); 728 // Thread.dumpStack(); 729 } 730 731 try { 732 if( 0 != preWinHandle ) { 733 setWindowHandle(0); 734 if( 0 == surfaceHandle ) { 735 throw new NativeWindowException("Internal Error - create w/ window, but no Newt NSView"); 736 } 737 OSXUtil.RunOnMainThread(false, false /* kickNSApp */, new Runnable() { 738 @Override 739 public void run() { 740 changeContentView0(parentWinHandle, preWinHandle, 0); 741 close0( preWinHandle ); 742 } }); 743 } else { 744 if( 0 != surfaceHandle ) { 745 throw new NativeWindowException("Internal Error - create w/o window, but has Newt NSView"); 746 } 747 surfaceHandle = createView0(pS.getX(), pS.getY(), width, height); 748 if( 0 == surfaceHandle ) { 749 throw new NativeWindowException("Could not create native view "+Thread.currentThread().getName()+" "+this); 750 } 751 } 752 753 final int windowStyle; 754 { 755 int ws = 0; 756 if( 0 != ( STATE_MASK_UNDECORATED & flags) || offscreenInstance ) { 757 ws = NSBorderlessWindowMask; 758 } else { 759 ws = NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask; 760 if( 0 != ( STATE_MASK_RESIZABLE & flags) ) { 761 ws |= NSResizableWindowMask; 762 } 763 } 764 windowStyle = ws; 765 } 766 final long newWin = createWindow0( pS.getX(), pS.getY(), width, height, 767 0 != ( STATE_MASK_FULLSCREEN & flags), 768 windowStyle, 769 NSBackingStoreBuffered, surfaceHandle); 770 if ( newWin == 0 ) { 771 throw new NativeWindowException("Could not create native window "+Thread.currentThread().getName()+" "+this); 772 } 773 setWindowHandle( newWin ); 774 775 final boolean isOpaque = getGraphicsConfiguration().getChosenCapabilities().isBackgroundOpaque() && !offscreenInstance; 776 // Blocking initialization on main-thread! 777 OSXUtil.RunOnMainThread(true, false /* kickNSApp */, new Runnable() { 778 @Override 779 public void run() { 780 initWindow0( parentWinHandle, newWin, pS.getX(), pS.getY(), width, height, reqPixelScale[0] /* HiDPI uniformPixelScale */, 781 isOpaque, 782 !offscreenInstance && 0 != ( STATE_MASK_ALWAYSONTOP & flags), 783 !offscreenInstance && 0 != ( STATE_MASK_ALWAYSONBOTTOM & flags), 784 !offscreenInstance && 0 != ( STATE_MASK_VISIBLE & flags), 785 surfaceHandle); 786 if( offscreenInstance ) { 787 orderOut0(0!=parentWinHandle ? parentWinHandle : newWin); 788 } else { 789 setTitle0(newWin, getTitle()); 790 } 791 } }); 792 } catch (final Exception ie) { 793 ie.printStackTrace(); 794 } 795 } 796 initIDs0()797 protected static native boolean initIDs0(); createView0(int x, int y, int w, int h)798 private native long createView0(int x, int y, int w, int h); createWindow0(int x, int y, int w, int h, boolean fullscreen, int windowStyle, int backingStoreType, long view)799 private native long createWindow0(int x, int y, int w, int h, boolean fullscreen, int windowStyle, int backingStoreType, long view); 800 /** Must be called on Main-Thread */ initWindow0(long parentWindow, long window, int x, int y, int w, int h, float reqPixelScale, boolean opaque, boolean atop, boolean abottom, boolean visible, long view)801 private native void initWindow0(long parentWindow, long window, int x, int y, int w, int h, float reqPixelScale, 802 boolean opaque, boolean atop, boolean abottom, boolean visible, long view); 803 getDisplayID0(long window)804 private native int getDisplayID0(long window); setPixelScale0(long window, long view, float reqPixelScale)805 private native void setPixelScale0(long window, long view, float reqPixelScale); lockSurface0(long window, long view)806 private native boolean lockSurface0(long window, long view); unlockSurface0(long window, long view)807 private native boolean unlockSurface0(long window, long view); 808 /** Must be called on Main-Thread */ requestFocus0(long window, boolean force)809 private native void requestFocus0(long window, boolean force); 810 /** Must be called on Main-Thread */ resignFocus0(long window)811 private native void resignFocus0(long window); 812 /** Must be called on Main-Thread. In case this is a child window and parent is still visible, orderBack(..) is issued instead of orderOut(). */ orderOut0(long window)813 private native void orderOut0(long window); 814 /** Must be called on Main-Thread */ orderFront0(long window)815 private native void orderFront0(long window); 816 /** Must be called on Main-Thread */ close0(long window)817 private native void close0(long window); 818 /** Must be called on Main-Thread */ setTitle0(long window, String title)819 private native void setTitle0(long window, String title); contentView0(long window)820 private native long contentView0(long window); 821 /** Must be called on Main-Thread */ changeContentView0(long parentWindowOrView, long window, long view)822 private native void changeContentView0(long parentWindowOrView, long window, long view); 823 /** Must be called on Main-Thread */ setWindowClientTopLeftPointAndSize0(long window, int x, int y, int w, int h, boolean display)824 private native void setWindowClientTopLeftPointAndSize0(long window, int x, int y, int w, int h, boolean display); 825 /** Must be called on Main-Thread */ setWindowClientTopLeftPoint0(long window, int x, int y, boolean display)826 private native void setWindowClientTopLeftPoint0(long window, int x, int y, boolean display); 827 /** Triggers {@link #sizeScreenPosInsetsChanged(boolean, int, int, int, int, int, int, int, int, boolean)} */ updateSizePosInsets0(long window, boolean defer)828 private native void updateSizePosInsets0(long window, boolean defer); getLocationOnScreen0(long windowHandle, int src_x, int src_y)829 private static native Object getLocationOnScreen0(long windowHandle, int src_x, int src_y); setPointerIcon0(long windowHandle, long handle)830 private static native void setPointerIcon0(long windowHandle, long handle); setPointerVisible0(long windowHandle, boolean hasFocus, boolean visible)831 private static native void setPointerVisible0(long windowHandle, boolean hasFocus, boolean visible); confinePointer0(long windowHandle, boolean confine)832 private static native void confinePointer0(long windowHandle, boolean confine); warpPointer0(long windowHandle, int x, int y)833 private static native void warpPointer0(long windowHandle, int x, int y); 834 835 // Window styles 836 private static final int NSBorderlessWindowMask = 0; 837 private static final int NSTitledWindowMask = 1 << 0; 838 private static final int NSClosableWindowMask = 1 << 1; 839 private static final int NSMiniaturizableWindowMask = 1 << 2; 840 private static final int NSResizableWindowMask = 1 << 3; 841 842 // Window backing store types 843 private static final int NSBackingStoreRetained = 0; 844 private static final int NSBackingStoreNonretained = 1; 845 private static final int NSBackingStoreBuffered = 2; 846 847 private volatile long surfaceHandle = 0; 848 private long sscSurfaceHandle = 0; 849 private boolean isOffscreenInstance = false; 850 851 } 852