run.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637
  1. // Copyright 2014 Hajime Hoshi
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package ebiten
  15. import (
  16. "sync/atomic"
  17. "github.com/hajimehoshi/ebiten/internal/clock"
  18. "github.com/hajimehoshi/ebiten/internal/driver"
  19. )
  20. // Game defines necessary functions for a game.
  21. type Game interface {
  22. // Update updates a game by one tick. The given argument represents a screen image.
  23. //
  24. // Basically Update updates the game logic. Whether Update also draws the screen or not depends on the
  25. // existence of Draw implementation.
  26. //
  27. // The Draw function's definition is:
  28. //
  29. // Draw(screen *Image)
  30. //
  31. // With Draw (the recommended way), Update updates only the game logic and Draw draws the screen.
  32. // In this case, the argument screen's updated content by Update is not adopted for the actual game screen,
  33. // and the screen's updated content by Draw is adopted instead.
  34. // In the first frame, it is ensured that Update is called at least once before Draw. You can use Update
  35. // to initialize the game state. After the first frame, Update might not be called or might be called once
  36. // or more for one frame. The frequency is determined by the current TPS (tick-per-second).
  37. //
  38. // Without Draw (the legacy way), Update updates the game logic and also draws the screen.
  39. // In this case, the argument screen's updated content by Update is adopted for the actual game screen.
  40. Update(screen *Image) error
  41. // Draw draws the game screen by one frame.
  42. //
  43. // The give argument represents a screen image. The updated content is adopted as the game screen.
  44. //
  45. // Draw is an optional function for backward compatibility.
  46. //
  47. // Draw(screen *Image)
  48. // Layout accepts a native outside size in device-independent pixels and returns the game's logical screen
  49. // size.
  50. //
  51. // On desktops, the outside is a window or a monitor (fullscreen mode). On browsers, the outside is a body
  52. // element. On mobiles, the outside is the view's size.
  53. //
  54. // Even though the outside size and the screen size differ, the rendering scale is automatically adjusted to
  55. // fit with the outside.
  56. //
  57. // Layout is called almost every frame.
  58. //
  59. // If Layout returns non-positive numbers, the caller can panic.
  60. //
  61. // You can return a fixed screen size if you don't care, or you can also return a calculated screen size
  62. // adjusted with the given outside size.
  63. Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int)
  64. }
  65. // DefaultTPS represents a default ticks per second, that represents how many times game updating happens in a second.
  66. const DefaultTPS = 60
  67. // FPS represents the default TPS (tick per second). This is for backward compatibility.
  68. //
  69. // Deprecated: (as of 1.8.0) Use DefaultTPS instead.
  70. const FPS = DefaultTPS
  71. // CurrentFPS returns the current number of FPS (frames per second), that represents
  72. // how many swapping buffer happens per second.
  73. //
  74. // On some environments, CurrentFPS doesn't return a reliable value since vsync doesn't work well there.
  75. // If you want to measure the application's speed, Use CurrentTPS.
  76. //
  77. // CurrentFPS is concurrent-safe.
  78. func CurrentFPS() float64 {
  79. return clock.CurrentFPS()
  80. }
  81. var (
  82. isDrawingSkipped = int32(0)
  83. isScreenClearedEveryFrame = int32(1)
  84. currentMaxTPS = int32(DefaultTPS)
  85. )
  86. func setDrawingSkipped(skipped bool) {
  87. v := int32(0)
  88. if skipped {
  89. v = 1
  90. }
  91. atomic.StoreInt32(&isDrawingSkipped, v)
  92. }
  93. // SetScreenClearedEveryFrame enables or disables the clearing of the screen at the beginning of each frame.
  94. // The default value is true and the screen is cleared each frame by default.
  95. //
  96. // SetScreenClearedEveryFrame is concurrent-safe.
  97. func SetScreenClearedEveryFrame(cleared bool) {
  98. v := int32(0)
  99. if cleared {
  100. v = 1
  101. }
  102. atomic.StoreInt32(&isScreenClearedEveryFrame, v)
  103. theUIContext.setScreenClearedEveryFrame(cleared)
  104. }
  105. // IsScreenClearedEveryFrame returns true if the frame isn't cleared at the beginning.
  106. //
  107. // IsScreenClearedEveryFrame is concurrent-safe.
  108. func IsScreenClearedEveryFrame() bool {
  109. return atomic.LoadInt32(&isScreenClearedEveryFrame) != 0
  110. }
  111. // IsDrawingSkipped returns true if rendering result is not adopted.
  112. // It is recommended to skip drawing images or screen
  113. // when IsDrawingSkipped is true.
  114. //
  115. // The typical code with IsDrawingSkipped is this:
  116. //
  117. // func update(screen *ebiten.Image) error {
  118. //
  119. // // Update the state.
  120. //
  121. // // When IsDrawingSkipped is true, the rendered result is not adopted.
  122. // // Skip rendering then.
  123. // if ebiten.IsDrawingSkipped() {
  124. // return nil
  125. // }
  126. //
  127. // // Draw something to the screen.
  128. //
  129. // return nil
  130. // }
  131. //
  132. // IsDrawingSkipped is useful if you use Run function or RunGame function without implementing Game's Draw.
  133. // Otherwise, i.e., if you use RunGame function with implementing Game's Draw, IsDrawingSkipped should not be used.
  134. // If you use RunGame and Draw, IsDrawingSkipped always returns true.
  135. //
  136. // IsDrawingSkipped is concurrent-safe.
  137. func IsDrawingSkipped() bool {
  138. return atomic.LoadInt32(&isDrawingSkipped) != 0
  139. }
  140. // IsRunningSlowly is an old name for IsDrawingSkipped.
  141. //
  142. // Deprecated: (as of 1.8.0) Use Game's Draw function instead.
  143. func IsRunningSlowly() bool {
  144. return IsDrawingSkipped()
  145. }
  146. // Run starts the main loop and runs the game.
  147. //
  148. // Deprecated: (as of 1.12.0) Use RunGame instead.
  149. //
  150. // f is a function which is called at every frame.
  151. // The argument (*Image) is the render target that represents the screen.
  152. // The screen size is based on the given values (width and height).
  153. //
  154. // Run is a shorthand for RunGame, but there are some restrictions.
  155. // If you want to resize the window by dragging, use RunGame instead.
  156. //
  157. // A window size is based on the given values (width, height and scale).
  158. //
  159. // scale is used to enlarge the screen on desktops.
  160. // scale is ignored on browsers or mobiles.
  161. // Note that the actual screen is multiplied not only by the given scale but also
  162. // by the device scale on high-DPI display.
  163. // If you pass inverse of the device scale,
  164. // you can disable this automatical device scaling as a result.
  165. // You can get the device scale by DeviceScaleFactor function.
  166. //
  167. // On browsers, the scale is automatically adjusted.
  168. // It is strongly recommended to use iframe if you embed an Ebiten application in your website.
  169. // scale works as this as of 1.10.0-alpha.
  170. // Before that, scale affected the rendering scale.
  171. //
  172. // On mobiles, if you use ebitenmobile command, the scale is automatically adjusted.
  173. //
  174. // Run must be called on the main thread.
  175. // Note that Ebiten bounds the main goroutine to the main OS thread by runtime.LockOSThread.
  176. //
  177. // Ebiten tries to call f 60 times a second by default. In other words,
  178. // TPS (ticks per second) is 60 by default.
  179. // This is not related to framerate (display's refresh rate).
  180. //
  181. // f is not called when the window is in background by default.
  182. // This setting is configurable with SetRunnableOnUnfocused.
  183. //
  184. // The given scale is ignored on fullscreen mode or gomobile-build mode.
  185. //
  186. // On non-GopherJS environments, Run returns error when 1) OpenGL error happens, 2) audio error happens or
  187. // 3) f returns error. In the case of 3), Run returns the same error.
  188. //
  189. // On GopherJS, Run returns immediately.
  190. // It is because the 'main' goroutine cannot be blocked on GopherJS due to the bug (gopherjs/gopherjs#826).
  191. // When an error happens, this is shown as an error on the console.
  192. //
  193. // The size unit is device-independent pixel.
  194. //
  195. // Don't call Run twice or more in one process.
  196. func Run(f func(*Image) error, width, height int, scale float64, title string) error {
  197. if IsWindowResizable() {
  198. panic("ebiten: a resizable window works with RunGame, not Run")
  199. }
  200. game := &defaultGame{
  201. update: (&imageDumper{f: f}).update,
  202. width: width,
  203. height: height,
  204. }
  205. ww, wh := int(float64(width)*scale), int(float64(height)*scale)
  206. fixWindowPosition(ww, wh)
  207. SetWindowSize(ww, wh)
  208. SetWindowTitle(title)
  209. return runGame(game, scale)
  210. }
  211. type imageDumperGame struct {
  212. game Game
  213. d *imageDumper
  214. }
  215. func (i *imageDumperGame) Update(screen *Image) error {
  216. if i.d == nil {
  217. i.d = &imageDumper{f: i.game.Update}
  218. }
  219. return i.d.update(screen)
  220. }
  221. func (i *imageDumperGame) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
  222. return i.game.Layout(outsideWidth, outsideHeight)
  223. }
  224. type imageDumperGameWithDraw struct {
  225. imageDumperGame
  226. err error
  227. }
  228. func (i *imageDumperGameWithDraw) Update(screen *Image) error {
  229. if i.err != nil {
  230. return i.err
  231. }
  232. return i.imageDumperGame.Update(screen)
  233. }
  234. func (i *imageDumperGameWithDraw) Draw(screen *Image) {
  235. if i.err != nil {
  236. return
  237. }
  238. i.game.(interface{ Draw(*Image) }).Draw(screen)
  239. // Call dump explicitly. IsDrawingSkipped always returns true when Draw is defined.
  240. if i.d == nil {
  241. i.d = &imageDumper{f: i.game.Update}
  242. }
  243. i.err = i.d.dump(screen)
  244. }
  245. // RunGame starts the main loop and runs the game.
  246. // game's Update function is called every tick to update the game logic.
  247. // game's Draw function is, if it exists, called every frame to draw the screen.
  248. // game's Layout function is called when necessary, and you can specify the logical screen size by the function.
  249. //
  250. // game must implement Game interface.
  251. // Game's Draw function is optional, but it is recommended to implement Draw to seperate updating the logic and
  252. // rendering.
  253. //
  254. // RunGame is a more flexibile form of Run due to game's Layout function.
  255. // You can make a resizable window if you use RunGame, while you cannot if you use Run.
  256. // RunGame is more sophisticated way than Run and hides the notion of 'scale'.
  257. //
  258. // While Run specifies the window size, RunGame does not.
  259. // You need to call SetWindowSize before RunGame if you want.
  260. // Otherwise, a default window size is adopted.
  261. //
  262. // Some functions (ScreenScale, SetScreenScale, SetScreenSize) are not available with RunGame.
  263. //
  264. // On browsers, it is strongly recommended to use iframe if you embed an Ebiten application in your website.
  265. //
  266. // RunGame must be called on the main thread.
  267. // Note that Ebiten bounds the main goroutine to the main OS thread by runtime.LockOSThread.
  268. //
  269. // Ebiten tries to call game's Update function 60 times a second by default. In other words,
  270. // TPS (ticks per second) is 60 by default.
  271. // This is not related to framerate (display's refresh rate).
  272. //
  273. // game's Update is not called when the window is in background by default.
  274. // This setting is configurable with SetRunnableOnUnfocused.
  275. //
  276. // On non-GopherJS environments, RunGame returns error when 1) OpenGL error happens, 2) audio error happens or
  277. // 3) f returns error. In the case of 3), RunGame returns the same error.
  278. //
  279. // On GopherJS, RunGame returns immediately.
  280. // It is because the 'main' goroutine cannot be blocked on GopherJS due to the bug (gopherjs/gopherjs#826).
  281. // When an error happens, this is shown as an error on the console.
  282. //
  283. // The size unit is device-independent pixel.
  284. //
  285. // Don't call RunGame twice or more in one process.
  286. func RunGame(game Game) error {
  287. fixWindowPosition(WindowSize())
  288. if _, ok := game.(interface{ Draw(*Image) }); ok {
  289. return runGame(&imageDumperGameWithDraw{
  290. imageDumperGame: imageDumperGame{game: game},
  291. }, 0)
  292. }
  293. return runGame(&imageDumperGame{game: game}, 0)
  294. }
  295. func runGame(game Game, scale float64) error {
  296. theUIContext.set(game, scale)
  297. if err := uiDriver().Run(theUIContext); err != nil {
  298. if err == driver.RegularTermination {
  299. return nil
  300. }
  301. return err
  302. }
  303. return nil
  304. }
  305. // RunGameWithoutMainLoop runs the game, but don't call the loop on the main (UI) thread.
  306. // Different from Run, RunGameWithoutMainLoop returns immediately.
  307. //
  308. // Ebiten users should NOT call RunGameWithoutMainLoop.
  309. // Instead, functions in github.com/hajimehoshi/ebiten/mobile package calls this.
  310. func RunGameWithoutMainLoop(game Game) {
  311. fixWindowPosition(WindowSize())
  312. if _, ok := game.(interface{ Draw(*Image) }); ok {
  313. game = &imageDumperGameWithDraw{
  314. imageDumperGame: imageDumperGame{game: game},
  315. }
  316. } else {
  317. game = &imageDumperGame{game: game}
  318. }
  319. theUIContext.set(game, 0)
  320. uiDriver().RunWithoutMainLoop(theUIContext)
  321. }
  322. // ScreenSizeInFullscreen returns the size in device-independent pixels when the game is fullscreen.
  323. // The adopted monitor is the 'current' monitor which the window belongs to.
  324. // The returned value can be given to Run or SetSize function if the perfectly fit fullscreen is needed.
  325. //
  326. // On browsers, ScreenSizeInFullscreen returns the 'window' (global object) size, not 'screen' size since an Ebiten
  327. // game should not know the outside of the window object. For more details, see SetFullscreen API comment.
  328. //
  329. // On mobiles, ScreenSizeInFullscreen returns (0, 0) so far.
  330. //
  331. // ScreenSizeInFullscreen's use cases are limited. If you are making a fullscreen application, you can use RunGame and
  332. // the Game interface's Layout function instead. If you are making a not-fullscreen application but the application's
  333. // behavior depends on the monitor size, ScreenSizeInFullscreen is useful.
  334. //
  335. // ScreenSizeInFullscreen must be called on the main thread before ebiten.Run, and is concurrent-safe after
  336. // ebiten.Run.
  337. func ScreenSizeInFullscreen() (int, int) {
  338. return uiDriver().ScreenSizeInFullscreen()
  339. }
  340. // MonitorSize is an old name for ScreenSizeInFullscreen.
  341. //
  342. // Deprecated: (as of 1.8.0) Use ScreenSizeInFullscreen instead.
  343. func MonitorSize() (int, int) {
  344. return ScreenSizeInFullscreen()
  345. }
  346. // SetScreenSize sets the game screen size and resizes the window.
  347. //
  348. // Deprecated: (as of 1.11.0) Use SetWindowSize and RunGame (Game's Layout) instead.
  349. func SetScreenSize(width, height int) {
  350. if width <= 0 || height <= 0 {
  351. panic("ebiten: width and height must be positive")
  352. }
  353. theUIContext.setScreenSize(width, height)
  354. }
  355. // SetScreenScale sets the game screen scale and resizes the window.
  356. //
  357. // Deprecated: (as of 1.11.0-alpha). Use SetWindowSize instead.
  358. func SetScreenScale(scale float64) {
  359. if scale <= 0 {
  360. panic("ebiten: scale must be positive")
  361. }
  362. theUIContext.setScaleForWindow(scale)
  363. }
  364. // ScreenScale returns the game screen scale.
  365. //
  366. // Deprecated: (as of 1.11.0-alpha) Use WindowSize instead.
  367. func ScreenScale() float64 {
  368. return theUIContext.getScaleForWindow()
  369. }
  370. // CursorMode returns the current cursor mode.
  371. //
  372. // On browsers, only CursorModeVisible and CursorModeHidden are supported.
  373. //
  374. // CursorMode returns CursorModeHidden on mobiles.
  375. //
  376. // CursorMode is concurrent-safe.
  377. func CursorMode() CursorModeType {
  378. return CursorModeType(uiDriver().CursorMode())
  379. }
  380. // SetCursorMode sets the render and capture mode of the mouse cursor.
  381. // CursorModeVisible sets the cursor to always be visible.
  382. // CursorModeHidden hides the system cursor when over the window.
  383. // CursorModeCaptured hides the system cursor and locks it to the window.
  384. //
  385. // On browsers, only CursorModeVisible and CursorModeHidden are supported.
  386. //
  387. // SetCursorMode does nothing on mobiles.
  388. //
  389. // SetCursorMode is concurrent-safe.
  390. func SetCursorMode(mode CursorModeType) {
  391. uiDriver().SetCursorMode(driver.CursorMode(mode))
  392. }
  393. // IsCursorVisible reports whether the cursor is visible or not.
  394. //
  395. // Deprecated: (as of 1.11.0-alpha) Use CursorMode instead.
  396. func IsCursorVisible() bool {
  397. return CursorMode() == CursorModeVisible
  398. }
  399. // SetCursorVisible sets the cursor visibility.
  400. //
  401. // Deprecated: (as of 1.11.0-alpha) Use SetCursorMode instead.
  402. func SetCursorVisible(visible bool) {
  403. if visible {
  404. SetCursorMode(CursorModeVisible)
  405. } else {
  406. SetCursorMode(CursorModeHidden)
  407. }
  408. }
  409. // SetCursorVisibility sets the cursor visibility.
  410. //
  411. // Deprecated: (as of 1.6.0-alpha) Use SetCursorMode instead.
  412. func SetCursorVisibility(visible bool) {
  413. SetCursorVisible(visible)
  414. }
  415. // IsFullscreen reports whether the current mode is fullscreen or not.
  416. //
  417. // IsFullscreen always returns false on browsers.
  418. // IsFullscreen works as this as of 1.10.0-alpha.
  419. // Before that, IsFullscreen reported whether the current mode is fullscreen or not.
  420. //
  421. // IsFullscreen always returns false on mobiles.
  422. //
  423. // IsFullscreen is concurrent-safe.
  424. func IsFullscreen() bool {
  425. return uiDriver().IsFullscreen()
  426. }
  427. // SetFullscreen changes the current mode to fullscreen or not on desktops.
  428. //
  429. // On fullscreen mode, the game screen is automatically enlarged
  430. // to fit with the monitor. The current scale value is ignored.
  431. //
  432. // On desktops, Ebiten uses 'windowed' fullscreen mode, which doesn't change
  433. // your monitor's resolution.
  434. //
  435. // SetFullscreen does nothing on browsers.
  436. // SetFullscreen works as this as of 1.10.0-alpha.
  437. // Before that, SetFullscreen affected the fullscreen mode.
  438. //
  439. // SetFullscreen does nothing on mobiles.
  440. //
  441. // SetFullscreen does nothing on macOS when the window is fullscreened natively by the macOS desktop
  442. // instead of SetFullscreen(true).
  443. //
  444. // SetFullscreen is concurrent-safe.
  445. func SetFullscreen(fullscreen bool) {
  446. uiDriver().SetFullscreen(fullscreen)
  447. }
  448. // IsFocused returns a boolean value indicating whether
  449. // the game is in focus or in the foreground.
  450. //
  451. // IsFocused will only return true if IsRunnableOnUnfocused is false.
  452. //
  453. // IsFocused is concurrent-safe.
  454. func IsFocused() bool {
  455. return uiDriver().IsFocused()
  456. }
  457. // IsRunnableOnUnfocused returns a boolean value indicating whether
  458. // the game runs even in background.
  459. //
  460. // IsRunnableOnUnfocused is concurrent-safe.
  461. func IsRunnableOnUnfocused() bool {
  462. return uiDriver().IsRunnableOnUnfocused()
  463. }
  464. // IsRunnableInBackground is an old name for IsRunnableOnUnfocused.
  465. //
  466. // Deprecated: (as of 1.11.0) Use IsRunnableOnUnfocused instead.
  467. func IsRunnableInBackground() bool {
  468. return IsRunnableOnUnfocused()
  469. }
  470. // SetRunnableOnUnfocused sets the state if the game runs even in background.
  471. //
  472. // If the given value is true, the game runs in background e.g. when losing focus.
  473. // The initial state is false.
  474. //
  475. // Known issue: On browsers, even if the state is on, the game doesn't run in background tabs.
  476. // This is because browsers throttles background tabs not to often update.
  477. //
  478. // SetRunnableOnUnfocused does nothing on mobiles so far.
  479. //
  480. // SetRunnableOnUnfocused is concurrent-safe.
  481. func SetRunnableOnUnfocused(runnableOnUnfocused bool) {
  482. uiDriver().SetRunnableOnUnfocused(runnableOnUnfocused)
  483. }
  484. // SetRunnableInBackground is an old name for SetRunnableOnUnfocused.
  485. //
  486. // Deprecated: (as of 1.11.0-alpha) Use SetRunnableOnUnfocused instead.
  487. func SetRunnableInBackground(runnableInBackground bool) {
  488. SetRunnableOnUnfocused(runnableInBackground)
  489. }
  490. // DeviceScaleFactor returns a device scale factor value of the current monitor which the window belongs to.
  491. //
  492. // DeviceScaleFactor returns a meaningful value on high-DPI display environment,
  493. // otherwise DeviceScaleFactor returns 1.
  494. //
  495. // DeviceScaleFactor might panic on init function on some devices like Android.
  496. // Then, it is not recommended to call DeviceScaleFactor from init functions.
  497. //
  498. // DeviceScaleFactor must be called on the main thread before the main loop, and is concurrent-safe after the main loop.
  499. func DeviceScaleFactor() float64 {
  500. return uiDriver().DeviceScaleFactor()
  501. }
  502. // IsVsyncEnabled returns a boolean value indicating whether
  503. // the game uses the display's vsync.
  504. //
  505. // IsVsyncEnabled is concurrent-safe.
  506. func IsVsyncEnabled() bool {
  507. return uiDriver().IsVsyncEnabled()
  508. }
  509. // SetVsyncEnabled sets a boolean value indicating whether
  510. // the game uses the display's vsync.
  511. //
  512. // If the given value is true, the game tries to sync the display's refresh rate.
  513. // If false, the game ignores the display's refresh rate.
  514. // The initial value is true.
  515. // By disabling vsync, the game works more efficiently but consumes more CPU.
  516. //
  517. // Note that the state doesn't affect TPS (ticks per second, i.e. how many the run function is
  518. // updated per second).
  519. //
  520. // SetVsyncEnabled does nothing on mobiles so far.
  521. //
  522. // SetVsyncEnabled is concurrent-safe.
  523. func SetVsyncEnabled(enabled bool) {
  524. uiDriver().SetVsyncEnabled(enabled)
  525. }
  526. // MaxTPS returns the current maximum TPS.
  527. //
  528. // MaxTPS is concurrent-safe.
  529. func MaxTPS() int {
  530. return int(atomic.LoadInt32(&currentMaxTPS))
  531. }
  532. // CurrentTPS returns the current TPS (ticks per second),
  533. // that represents how many update function is called in a second.
  534. //
  535. // CurrentTPS is concurrent-safe.
  536. func CurrentTPS() float64 {
  537. return clock.CurrentTPS()
  538. }
  539. // UncappedTPS is a special TPS value that means the game doesn't have limitation on TPS.
  540. const UncappedTPS = clock.UncappedTPS
  541. // SetMaxTPS sets the maximum TPS (ticks per second),
  542. // that represents how many updating function is called per second.
  543. // The initial value is 60.
  544. //
  545. // If tps is UncappedTPS, TPS is uncapped and the game is updated per frame.
  546. // If tps is negative but not UncappedTPS, SetMaxTPS panics.
  547. //
  548. // SetMaxTPS is concurrent-safe.
  549. func SetMaxTPS(tps int) {
  550. if tps < 0 && tps != UncappedTPS {
  551. panic("ebiten: tps must be >= 0 or UncappedTPS")
  552. }
  553. atomic.StoreInt32(&currentMaxTPS, int32(tps))
  554. }
  555. // IsScreenTransparent reports whether the window is transparent.
  556. //
  557. // IsScreenTransparent is concurrent-safe.
  558. func IsScreenTransparent() bool {
  559. return uiDriver().IsScreenTransparent()
  560. }
  561. // SetScreenTransparent sets the state if the window is transparent.
  562. //
  563. // SetScreenTransparent panics if SetScreenTransparent is called after the main loop.
  564. //
  565. // SetScreenTransparent does nothing on mobiles.
  566. //
  567. // SetScreenTransparent is concurrent-safe.
  568. func SetScreenTransparent(transparent bool) {
  569. uiDriver().SetScreenTransparent(transparent)
  570. }
  571. // SetInitFocused sets whether the application is focused on show.
  572. // The default value is true, i.e., the application is focused.
  573. // Note that the application does not proceed if this is not focused by default.
  574. // This behavior can be changed by SetRunnableInBackground.
  575. //
  576. // SetInitFocused does nothing on mobile.
  577. //
  578. // SetInitFocused panics if this is called after the main loop.
  579. //
  580. // SetInitFocused is cuncurrent-safe.
  581. func SetInitFocused(focused bool) {
  582. uiDriver().SetInitFocused(focused)
  583. }