window.monkey2 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736
  1. Namespace mojo.app
  2. #If __TARGET__="macos"
  3. #Import "native/displaylink.mm"
  4. #Import "native/displaylink.h"
  5. #Endif
  6. Extern Private
  7. #If __TARGET__="macos"
  8. Function initDisplayLink()="bbDisplayLink::init"
  9. Function enableDisplayLink(enable:Bool)="bbDisplayLink::enable"
  10. #Endif
  11. Public
  12. #rem monkeydoc Window creation flags.
  13. | WindowFlags | Description
  14. |:--------------|:-----------
  15. | CenterX | Center window horizontally.
  16. | CenterY | Center window vertically.
  17. | Center | Center window.
  18. | Hidden | Window is initally hidden.
  19. | Resizable | Window is resizable.
  20. | Fullscreen | Window is a fullscreen window.
  21. | Maximized | Window is maximized.
  22. | Minimized | Window is minimized.
  23. #end
  24. Enum WindowFlags
  25. CenterX=1
  26. CenterY=2
  27. Hidden=4
  28. Resizable=8
  29. Borderless=16
  30. Fullscreen=32
  31. HighDPI=64
  32. Maximized=128
  33. Minimized=256
  34. Center=CenterX|CenterY
  35. End
  36. #rem monkeydoc The Window class.
  37. #end
  38. Class Window Extends View
  39. #rem monkeydoc Creates a new window.
  40. #end
  41. Method New( title:String="Window",width:Int=640,height:Int=480,flags:WindowFlags=Null )
  42. Init( title,New Recti( 0,0,width,height ),flags|WindowFlags.Center )
  43. End
  44. Method New( title:String,rect:Recti,flags:WindowFlags=Null )
  45. Init( title,rect,flags )
  46. End
  47. #rem monkeydoc The window title text.
  48. #end
  49. Property Title:String()
  50. Return String.FromCString( SDL_GetWindowTitle( _sdlWindow ) )
  51. Setter( title:String )
  52. SDL_SetWindowTitle( _sdlWindow,title )
  53. End
  54. #rem monkeydoc The window clear color.
  55. #end
  56. Property ClearColor:Color()
  57. Return _clearColor
  58. Setter( clearColor:Color )
  59. _clearColor=clearColor
  60. End
  61. #rem monkeydoc True if window clearing is enabled.
  62. #end
  63. Property ClearEnabled:Bool()
  64. Return _clearEnabled
  65. Setter( clearEnabled:Bool )
  66. _clearEnabled=clearEnabled
  67. End
  68. #rem monkeydoc The window swap interval.
  69. #end
  70. Property SwapInterval:Int()
  71. Return _swapInterval
  72. Setter( swapInterval:Int )
  73. If swapInterval=_swapInterval Return
  74. _swapInterval=swapInterval
  75. #If __TARGET__="macos"
  76. _canRender=True
  77. #Endif
  78. End
  79. 'Deprecated! Too hard on macos...
  80. Property SwapAsync:Bool()
  81. Return False
  82. Setter( swapAsync:Bool )
  83. End
  84. Property CanRender:Bool()
  85. Return _canRender
  86. End
  87. #rem monkeydoc Window fullscreen state.
  88. Note: The setter for this property deprecated! Please use BeginFullscreen/EndFullscreen instead.
  89. #end
  90. Property Fullscreen:Bool()
  91. Return Cast<SDL_WindowFlags>( SDL_GetWindowFlags( _sdlWindow ) ) & SDL_WINDOW_FULLSCREEN
  92. Setter( fullscreen:Bool )
  93. If fullscreen=Fullscreen Return
  94. If fullscreen BeginFullscreen() Else EndFullscreen()
  95. End
  96. #rem monkeydoc Window maximized state.
  97. #end
  98. Property Maximized:Bool()
  99. Return Cast<SDL_WindowFlags>( SDL_GetWindowFlags( _sdlWindow ) ) & SDL_WINDOW_MAXIMIZED
  100. End
  101. #rem monkeydoc Window minimized state.
  102. #end
  103. Property Minimized:Bool()
  104. Return Cast<SDL_WindowFlags>( SDL_GetWindowFlags( _sdlWindow ) ) & SDL_WINDOW_MINIMIZED
  105. End
  106. #rem monkeydoc Window content view.
  107. During layout, the window's content view is resized to fill the window.
  108. #end
  109. Property ContentView:View()
  110. Return _contentView
  111. Setter( contentView:View )
  112. If _contentView RemoveChildView( _contentView )
  113. _contentView=contentView
  114. If _contentView AddChildView( _contentView )
  115. End
  116. Method ResizeWindow( rect:Recti )
  117. Local x:Int=rect.Left/_mouseScale.x
  118. Local y:Int=rect.Top/_mouseScale.y
  119. Local w:Int=rect.Right/_mouseScale.x-x
  120. Local h:Int=rect.Bottom/_mouseScale.y-y
  121. SDL_SetWindowPosition( _sdlWindow,x,y )
  122. SDL_SetWindowSize( _sdlWindow,w,h )
  123. Frame=GetFrame()
  124. _frame=Frame
  125. _weirdHack=True
  126. End
  127. Method ResizeWindow( x:Int,y:Int,width:Int,height:Int )
  128. ResizeWindow( New Recti( x,y,x+width,y+height ) )
  129. End
  130. Method ResizeWindow( width:Int,height:Int )
  131. ResizeWindow( New Recti( 0,0,width,height ).Centered( New Recti( 0,0,App.DesktopSize ) ) )
  132. End
  133. #rem monkeydoc Switches window to fullscreen mode.
  134. If called with no parameters, resizes the window to cover the entire desktop without actually changing the display mode.
  135. If called with size and refresh rate parameters, both the display mode and window size are changed.
  136. #end
  137. Method BeginFullscreen()
  138. SDL_SetWindowFullscreen( _sdlWindow,SDL_WINDOW_FULLSCREEN_DESKTOP )
  139. End
  140. #rem monkeydoc Changes display mode and switches to fullscreen mode.
  141. The display resolution is changed, the window is resized and any window decorations are hidden.
  142. #end
  143. Method BeginFullscreen( width:Int,height:Int,hertz:Int )
  144. Local mode:SDL_DisplayMode
  145. mode.w=width
  146. mode.h=height
  147. mode.refresh_rate=hertz
  148. Local closest:SDL_DisplayMode
  149. SDL_GetClosestDisplayMode( 0,Varptr mode,Varptr closest )
  150. SDL_SetWindowDisplayMode( _sdlWindow,Varptr closest )
  151. SDL_SetWindowFullscreen( _sdlWindow,SDL_WINDOW_FULLSCREEN )
  152. End
  153. #rem monkeydoc Ends fullscreen mode.
  154. #end
  155. Method EndFullscreen()
  156. SDL_SetWindowFullscreen( _sdlWindow,0 )
  157. End
  158. #rem monkeydoc Maximizes the window.
  159. #end
  160. Method Maximize()
  161. SDL_MaximizeWindow( _sdlWindow )
  162. End
  163. #rem monkeydoc Minimizes the window.
  164. #end
  165. Method Minimize()
  166. SDL_MinimizeWindow( _sdlWindow )
  167. End
  168. #rem monkeydoc Restores the window.
  169. #end
  170. Method Restore()
  171. SDL_RestoreWindow( _sdlWindow )
  172. End
  173. '***** INTERNAL *****
  174. #rem monkeydoc @hidden
  175. #End
  176. Method UpdateWindow( render:Bool )
  177. LayoutWindow()
  178. If render RenderWindow()
  179. End
  180. #rem monkeydoc @hidden Mouse scale for ios retina devices. Should prob. be in App so @2.png can use it etc.
  181. #end
  182. Property MouseScale:Vec2f()
  183. Return _mouseScale
  184. End
  185. #rem monkeydoc @hidden The internal SDL_Window used by this window.
  186. #end
  187. Property SDLWindow:SDL_Window Ptr()
  188. Return _sdlWindow
  189. End
  190. #rem monkeydoc @hidden The internal SDL_GLContext used by this window.
  191. #end
  192. Property SDLGLContext:SDL_GLContext()
  193. Return _sdlGLContext
  194. End
  195. #rem monkeydoc @hidden
  196. #end
  197. Function AllWindows:Window[]()
  198. Return _allWindows.ToArray()
  199. End
  200. #rem monkeydoc @hidden
  201. #end
  202. Function VisibleWindows:Window[]()
  203. Return _visibleWindows.ToArray()
  204. End
  205. #rem monkeydoc @hidden
  206. #end
  207. Function WindowForID:Window( id:UInt )
  208. Return _windowsByID[id]
  209. End
  210. #rem monkeydoc @hidden
  211. #end
  212. Method SendTouchEvent( event:TouchEvent )
  213. OnTouchEvent( event )
  214. End
  215. #rem monkeydoc @hidden
  216. #end
  217. Method SendWindowEvent( event:WindowEvent )
  218. Select event.Type
  219. Case EventType.WindowMaximized
  220. Case EventType.WindowMinimized
  221. Case EventType.WindowRestored
  222. Case EventType.WindowSwapped
  223. Case EventType.WindowVSync
  224. #If __TARGET__="macos"
  225. _canRender=True
  226. #endif
  227. Case EventType.WindowMoved,EventType.WindowResized
  228. Frame=GetFrame()
  229. _frame=Frame
  230. _weirdHack=True
  231. End
  232. OnWindowEvent( event )
  233. End
  234. #rem monkeydoc Clear the window directly.
  235. ClearWindow can be used to clear the window outside of normal OnRender processing.
  236. #end
  237. Method ClearWindow( color:Color )
  238. SDL_GL_MakeCurrent( _sdlWindow,_sdlGLContext )
  239. Local bounds:=New Recti( 0,0,Frame.Size )
  240. _canvas.Resize( bounds.Size )
  241. _canvas.BeginRender( bounds,New AffineMat3f )
  242. _canvas.Clear( color )
  243. _canvas.EndRender()
  244. SDL_GL_SwapWindow( _sdlWindow )
  245. End
  246. Protected
  247. #rem monkeydoc Called once after a Window has been created.
  248. #end
  249. Method OnCreateWindow() Virtual
  250. End
  251. #rem monkeydoc Theme changed handler.
  252. Called when the App theme changes.
  253. #end
  254. Method OnThemeChanged() override
  255. _clearColor=App.Theme.GetColor( "windowClearColor" )
  256. End
  257. #rem monkeydoc Touch event handler.
  258. Called when the user touches the window on a touch compatible device.
  259. #end
  260. Method OnTouchEvent( event:TouchEvent ) Virtual
  261. End
  262. #rem monkeydoc Window event handler.
  263. Called when the window is sent a window event.
  264. #end
  265. Method OnWindowEvent( event:WindowEvent ) Virtual
  266. Select event.Type
  267. Case EventType.WindowClose
  268. App.Terminate()
  269. Case EventType.WindowResized
  270. App.RequestRender() 'Should maybe do this regardless?
  271. Case EventType.WindowGainedFocus
  272. App.RequestRender() 'Need to do this for KDE on linux?
  273. End
  274. End
  275. Method OnLayout() Override
  276. If _contentView _contentView.Frame=Rect
  277. End
  278. Internal
  279. Method CreateWindow()
  280. If _clearEnabled ClearWindow( _clearColor )
  281. OnCreateWindow()
  282. End
  283. Function CreateNewWindows()
  284. For Local window:=Eachin _newWindows
  285. window.CreateWindow()
  286. End
  287. _newWindows.Clear()
  288. End
  289. Private
  290. Field _sdlWindow:SDL_Window Ptr
  291. Field _sdlGLContext:SDL_GLContext
  292. Field _flags:WindowFlags
  293. Field _maxfudge:Int
  294. Field _rswapInterval:=1
  295. Field _swapInterval:=1
  296. Field _canRender:=True
  297. Field _canvas:Canvas
  298. Field _clearColor:=Color.Grey
  299. Field _clearEnabled:=True
  300. Field _contentView:View
  301. Field _minSize:Vec2i
  302. Field _maxSize:Vec2i
  303. Field _frame:Recti
  304. Field _mouseScale:=New Vec2f( 1,1 )
  305. 'Ok, angles glViewport appears To be 'lagging' by one frame, causing weirdness when resizing.
  306. Field _weirdHack:Bool
  307. Global _allWindows:=New Stack<Window>
  308. Global _visibleWindows:=New Stack<Window>
  309. Global _windowsByID:=New Map<UInt,Window>
  310. Global _newWindows:=New Stack<Window>
  311. Method GetDrawableSize:Vec2i()
  312. Local w:Int,h:Int
  313. #If __TARGET__="emscripten"
  314. Local d:Int
  315. emscripten_get_canvas_size( Varptr w,Varptr h,Varptr d )
  316. #Else
  317. SDL_GL_GetDrawableSize( _sdlWindow,Varptr w,Varptr h )
  318. #Endif
  319. Return New Vec2i( w,h )
  320. End
  321. 'Note: also updates _mouseScale
  322. '
  323. Method GetFrame:Recti()
  324. Local dsize:=GetDrawableSize()
  325. Local w:Int,h:Int
  326. SDL_GetWindowSize( _sdlWindow,Varptr w,Varptr h )
  327. _mouseScale=Cast<Vec2f>( dsize )/New Vec2f( w,h )
  328. #If __DESKTOP_TARGET__
  329. Local x:Int,y:Int
  330. SDL_GetWindowPosition( _sdlWindow,Varptr x,Varptr y )
  331. Local dpos:Vec2i=New Vec2f( x,y ) * _mouseScale
  332. Return New Recti( dpos,dpos+dsize )
  333. #else
  334. Return New Recti( 0,0,dsize )
  335. #endif
  336. End
  337. Method GetMinSize:Vec2i()
  338. Local w:Int,h:Int
  339. SDL_GetWindowMinimumSize( _sdlWindow,Varptr w,Varptr h )
  340. Return New Vec2f( w,h ) * _mouseScale
  341. End
  342. Method GetMaxSize:Vec2i()
  343. Local w:Int,h:Int
  344. SDL_GetWindowMaximumSize( _sdlWindow,Varptr w,Varptr h )
  345. Return New Vec2f( w,h ) * _mouseScale
  346. End
  347. Method SetFrame( rect:Recti )
  348. #If __DESKTOP_TARGET__
  349. rect=Cast<Rectf>( rect )/_mouseScale
  350. SDL_SetWindowPosition( _sdlWindow,rect.X,rect.Y )
  351. SDL_SetWindowSize( _sdlWindow,rect.Width,rect.Height )
  352. #endif
  353. End
  354. Method SetMinSize( size:Vec2i )
  355. #If __DESKTOP_TARGET__
  356. size=Cast<Vec2f>( size )/_mouseScale
  357. SDL_SetWindowMinimumSize( _sdlWindow,size.x,size.y )
  358. #endif
  359. End
  360. Method SetMaxSize( size:Vec2i )
  361. #If __DESKTOP_TARGET__
  362. size=Cast<Vec2f>( size )/_mouseScale
  363. SDL_SetWindowMaximumSize( _sdlWindow,size.x,size.y )
  364. #endif
  365. End
  366. Method LayoutWindow()
  367. 'All this polling is a bit ugly...fixme.
  368. '
  369. #If __DESKTOP_TARGET__
  370. If Frame<>_frame
  371. SetFrame( Frame )
  372. Frame=GetFrame()
  373. _frame=Frame
  374. _weirdHack=True
  375. Endif
  376. If MinSize<>_minSize
  377. SetMinSize( MinSize )
  378. MinSize=GetMinSize()
  379. _minSize=MinSize
  380. Endif
  381. If MaxSize<>_maxSize
  382. SetMaxSize( MaxSize )
  383. MaxSize=GetMaxSize()
  384. _maxSize=MaxSize
  385. Endif
  386. #else
  387. Frame=GetFrame()
  388. _frame=Frame
  389. #endif
  390. Measure()
  391. UpdateLayout()
  392. End
  393. #rem monkeydoc @hidden
  394. #end
  395. Method RenderWindow()
  396. Assert( _canRender )
  397. #If __TARGET__="macos"
  398. _canRender=(_swapInterval=0)
  399. #endif
  400. If _maxfudge
  401. _maxfudge-=1
  402. RequestRender()
  403. Endif
  404. SDL_GL_MakeCurrent( _sdlWindow,_sdlGLContext )
  405. If _swapInterval<>_rswapInterval
  406. _rswapInterval=_swapInterval
  407. #If __TARGET__="macos"
  408. enableDisplayLink(_swapInterval<>0)
  409. #Else
  410. SDL_GL_SetSwapInterval( _swapInterval )
  411. #endif
  412. Endif
  413. #If __TARGET__="windows"
  414. If _weirdHack
  415. _weirdHack=False
  416. Local attr:Int
  417. If SDL_GL_GetAttribute( SDL_GL_CONTEXT_PROFILE_MASK,Varptr attr )=0
  418. If attr=SDL_GL_CONTEXT_PROFILE_ES SDL_GL_SwapWindow( _sdlWindow )
  419. Endif
  420. Endif
  421. #Endif
  422. Local bounds:=New Recti( 0,0,Frame.Size )
  423. _canvas.Resize( bounds.Size )
  424. _canvas.BeginRender( bounds,New AffineMat3f )
  425. If _clearEnabled _canvas.Clear( _clearColor )
  426. Render( _canvas )
  427. _canvas.EndRender()
  428. SDL_GL_SwapWindow( _sdlWindow )
  429. #If __TARGET__="macos"
  430. glFinish()
  431. #Endif
  432. End
  433. Method Init( title:String,rect:Recti,flags:WindowFlags )
  434. Style=GetStyle( "Window" )
  435. If flags & WindowFlags.Hidden Visible=False
  436. Local x:=(flags & WindowFlags.CenterX) ? SDL_WINDOWPOS_CENTERED Else rect.X
  437. Local y:=(flags & WindowFlags.CenterY) ? SDL_WINDOWPOS_CENTERED Else rect.Y
  438. Local w:=rect.Width,h:=rect.Height
  439. Local sdlFlags:SDL_WindowFlags=SDL_WINDOW_OPENGL
  440. If flags & WindowFlags.Fullscreen
  441. sdlFlags|=SDL_WINDOW_FULLSCREEN
  442. Else If flags & WindowFlags.Maximized
  443. sdlFlags|=SDL_WINDOW_MAXIMIZED
  444. _maxfudge=2
  445. Else If flags & WindowFlags.Minimized
  446. sdlFlags|=SDL_WINDOW_MINIMIZED
  447. Endif
  448. If flags & WindowFlags.Hidden sdlFlags|=SDL_WINDOW_HIDDEN
  449. If flags & WindowFlags.Resizable sdlFlags|=SDL_WINDOW_RESIZABLE
  450. If flags & WindowFlags.Borderless sdlFlags|=SDL_WINDOW_BORDERLESS
  451. If flags & WindowFlags.HighDPI sdlFlags|=SDL_WINDOW_ALLOW_HIGHDPI
  452. _flags=flags
  453. 'Create Window
  454. _sdlWindow=SDL_CreateWindow( title,x,y,w,h,sdlFlags )
  455. If Not _sdlWindow
  456. Print "SDL_GetError="+String.FromCString( SDL_GetError() )
  457. Assert( _sdlWindow,"FATAL ERROR: SDL_CreateWindow failed" )
  458. Endif
  459. 'Create GL context
  460. _sdlGLContext=SDL_GL_CreateContext( _sdlWindow )
  461. If Not _sdlGLContext
  462. Print "SDL_GetError="+String.FromCString( SDL_GetError() )
  463. Assert( _sdlGLContext,"FATAL ERROR: SDL_GL_CreateContext failed" )
  464. Endif
  465. SDL_GL_MakeCurrent( _sdlWindow,_sdlGLContext )
  466. #If __TARGET__="macos"
  467. initDisplayLink()
  468. enableDisplayLink( _rswapInterval<>0 )
  469. SDL_GL_SetSwapInterval( 0 )
  470. #Else
  471. SDL_GL_SetSwapInterval( _rswapInterval )
  472. #endif
  473. bbglInit()
  474. _allWindows.Push( Self )
  475. _newWindows.Push( Self )
  476. _windowsByID[SDL_GetWindowID( _sdlWindow )]=Self
  477. If Not (flags & WindowFlags.Hidden) _visibleWindows.Push( Self )
  478. Frame=GetFrame()
  479. _frame=Frame
  480. 'UGLY!!!!!
  481. #If __DESKTOP_TARGET__
  482. If _mouseScale.x<>1 Or _mouseScale.y<>1
  483. Local x:Int=(flags & WindowFlags.CenterX) ? SDL_WINDOWPOS_CENTERED Else rect.X/_mouseScale.x
  484. Local y:Int=(flags & WindowFlags.CenterY) ? SDL_WINDOWPOS_CENTERED Else rect.Y/_mouseScale.y
  485. Local w:Int=rect.Width/_mouseScale.x
  486. Local h:Int=rect.Height/_mouseScale.y
  487. SDL_SetWindowPosition( _sdlWindow,x,y )
  488. SDL_SetWindowSize( _sdlWindow,w,h )
  489. Frame=GetFrame()
  490. _frame=Frame
  491. Endif
  492. #endif
  493. MinSize=GetMinSize()
  494. _minSize=MinSize
  495. MaxSize=GetMaxSize()
  496. _maxSize=MaxSize
  497. _clearColor=App.Theme.GetColor( "windowClearColor" )
  498. _canvas=New Canvas( _frame.Width,_frame.Height )
  499. SetWindow( Self )
  500. UpdateActive()
  501. LayoutWindow()
  502. Activated+=Lambda()
  503. Local flags:=Cast<SDL_WindowFlags>( SDL_GetWindowFlags( _sdlWindow ) )
  504. If (flags & SDL_WINDOW_HIDDEN)
  505. SDL_ShowWindow( _sdlWindow )
  506. SDL_RaiseWindow( _sdlWindow )
  507. _visibleWindows.Push( Self )
  508. Endif
  509. End
  510. Deactivated+=Lambda()
  511. RuntimeError( "Windows cannot be deactivated" )
  512. End
  513. End
  514. End