os.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. // SPDX-License-Identifier: Unlicense OR MIT
  2. package app
  3. import (
  4. "errors"
  5. "image"
  6. "image/color"
  7. "gioui.org/io/event"
  8. "gioui.org/io/key"
  9. "gioui.org/op"
  10. "gioui.org/gpu"
  11. "gioui.org/io/pointer"
  12. "gioui.org/io/system"
  13. "gioui.org/unit"
  14. )
  15. // errOutOfDate is reported when the GPU surface dimensions or properties no
  16. // longer match the window.
  17. var errOutOfDate = errors.New("app: GPU surface out of date")
  18. // Config describes a Window configuration.
  19. type Config struct {
  20. // Size is the window dimensions (Width, Height).
  21. Size image.Point
  22. // MaxSize is the window maximum allowed dimensions.
  23. MaxSize image.Point
  24. // MinSize is the window minimum allowed dimensions.
  25. MinSize image.Point
  26. // Title is the window title displayed in its decoration bar.
  27. Title string
  28. // WindowMode is the window mode.
  29. Mode WindowMode
  30. // StatusColor is the color of the Android status bar.
  31. StatusColor color.NRGBA
  32. // NavigationColor is the color of the navigation bar
  33. // on Android, or the address bar in browsers.
  34. NavigationColor color.NRGBA
  35. // Orientation is the current window orientation.
  36. Orientation Orientation
  37. // CustomRenderer is true when the window content is rendered by the
  38. // client.
  39. CustomRenderer bool
  40. // Decorated reports whether window decorations are provided automatically.
  41. Decorated bool
  42. // Focused reports whether has the keyboard focus.
  43. Focused bool
  44. // decoHeight is the height of the fallback decoration for platforms such
  45. // as Wayland that may need fallback client-side decorations.
  46. decoHeight unit.Dp
  47. }
  48. // ConfigEvent is sent whenever the configuration of a Window changes.
  49. type ConfigEvent struct {
  50. Config Config
  51. }
  52. func (c *Config) apply(m unit.Metric, options []Option) {
  53. for _, o := range options {
  54. o(m, c)
  55. }
  56. }
  57. type wakeupEvent struct{}
  58. // WindowMode is the window mode (WindowMode.Option sets it).
  59. // Note that mode can be changed programatically as well as by the user
  60. // clicking on the minimize/maximize buttons on the window's title bar.
  61. type WindowMode uint8
  62. const (
  63. // Windowed is the normal window mode with OS specific window decorations.
  64. Windowed WindowMode = iota
  65. // Fullscreen is the full screen window mode.
  66. Fullscreen
  67. // Minimized is for systems where the window can be minimized to an icon.
  68. Minimized
  69. // Maximized is for systems where the window can be made to fill the available monitor area.
  70. Maximized
  71. )
  72. // Option changes the mode of a Window.
  73. func (m WindowMode) Option() Option {
  74. return func(_ unit.Metric, cnf *Config) {
  75. cnf.Mode = m
  76. }
  77. }
  78. // String returns the mode name.
  79. func (m WindowMode) String() string {
  80. switch m {
  81. case Windowed:
  82. return "windowed"
  83. case Fullscreen:
  84. return "fullscreen"
  85. case Minimized:
  86. return "minimized"
  87. case Maximized:
  88. return "maximized"
  89. }
  90. return ""
  91. }
  92. // Orientation is the orientation of the app (Orientation.Option sets it).
  93. //
  94. // Supported platforms are Android and JS.
  95. type Orientation uint8
  96. const (
  97. // AnyOrientation allows the window to be freely orientated.
  98. AnyOrientation Orientation = iota
  99. // LandscapeOrientation constrains the window to landscape orientations.
  100. LandscapeOrientation
  101. // PortraitOrientation constrains the window to portrait orientations.
  102. PortraitOrientation
  103. )
  104. func (o Orientation) Option() Option {
  105. return func(_ unit.Metric, cnf *Config) {
  106. cnf.Orientation = o
  107. }
  108. }
  109. func (o Orientation) String() string {
  110. switch o {
  111. case AnyOrientation:
  112. return "any"
  113. case LandscapeOrientation:
  114. return "landscape"
  115. case PortraitOrientation:
  116. return "portrait"
  117. }
  118. return ""
  119. }
  120. // eventLoop implements the functionality required for drivers where
  121. // window event loops must run on a separate thread.
  122. type eventLoop struct {
  123. win *callbacks
  124. // wakeup is the callback to wake up the event loop.
  125. wakeup func()
  126. // driverFuncs is a channel of functions to run the next
  127. // time the window loop waits for events.
  128. driverFuncs chan func()
  129. // invalidates is notified when an invalidate is requested by the client.
  130. invalidates chan struct{}
  131. // immediateInvalidates is an optimistic invalidates that doesn't require a wakeup.
  132. immediateInvalidates chan struct{}
  133. // events is where the platform backend delivers events bound for the
  134. // user program.
  135. events chan event.Event
  136. frames chan *op.Ops
  137. frameAck chan struct{}
  138. // delivering avoids re-entrant event delivery.
  139. delivering bool
  140. }
  141. type frameEvent struct {
  142. FrameEvent
  143. Sync bool
  144. }
  145. type context interface {
  146. API() gpu.API
  147. RenderTarget() (gpu.RenderTarget, error)
  148. Present() error
  149. Refresh() error
  150. Release()
  151. Lock() error
  152. Unlock()
  153. }
  154. // driver is the interface for the platform implementation
  155. // of a window.
  156. type driver interface {
  157. // Event blocks until an event is available and returns it.
  158. Event() event.Event
  159. // Invalidate requests a FrameEvent.
  160. Invalidate()
  161. // SetAnimating sets the animation flag. When the window is animating,
  162. // FrameEvents are delivered as fast as the display can handle them.
  163. SetAnimating(anim bool)
  164. // ShowTextInput updates the virtual keyboard state.
  165. ShowTextInput(show bool)
  166. SetInputHint(mode key.InputHint)
  167. NewContext() (context, error)
  168. // ReadClipboard requests the clipboard content.
  169. ReadClipboard()
  170. // WriteClipboard requests a clipboard write.
  171. WriteClipboard(mime string, s []byte)
  172. // Configure the window.
  173. Configure([]Option)
  174. // SetCursor updates the current cursor to name.
  175. SetCursor(cursor pointer.Cursor)
  176. // Perform actions on the window.
  177. Perform(system.Action)
  178. // EditorStateChanged notifies the driver that the editor state changed.
  179. EditorStateChanged(old, new editorState)
  180. // Run a function on the window thread.
  181. Run(f func())
  182. // Frame receives a frame.
  183. Frame(frame *op.Ops)
  184. // ProcessEvent processes an event.
  185. ProcessEvent(e event.Event)
  186. }
  187. type windowRendezvous struct {
  188. in chan windowAndConfig
  189. out chan windowAndConfig
  190. windows chan struct{}
  191. }
  192. type windowAndConfig struct {
  193. window *callbacks
  194. options []Option
  195. }
  196. func newWindowRendezvous() *windowRendezvous {
  197. wr := &windowRendezvous{
  198. in: make(chan windowAndConfig),
  199. out: make(chan windowAndConfig),
  200. windows: make(chan struct{}),
  201. }
  202. go func() {
  203. in := wr.in
  204. var window windowAndConfig
  205. var out chan windowAndConfig
  206. for {
  207. select {
  208. case w := <-in:
  209. window = w
  210. out = wr.out
  211. case out <- window:
  212. }
  213. }
  214. }()
  215. return wr
  216. }
  217. func newEventLoop(w *callbacks, wakeup func()) *eventLoop {
  218. return &eventLoop{
  219. win: w,
  220. wakeup: wakeup,
  221. events: make(chan event.Event),
  222. invalidates: make(chan struct{}, 1),
  223. immediateInvalidates: make(chan struct{}),
  224. frames: make(chan *op.Ops),
  225. frameAck: make(chan struct{}),
  226. driverFuncs: make(chan func(), 1),
  227. }
  228. }
  229. // Frame receives a frame and waits for its processing. It is called by
  230. // the client goroutine.
  231. func (e *eventLoop) Frame(frame *op.Ops) {
  232. e.frames <- frame
  233. <-e.frameAck
  234. }
  235. // Event returns the next available event. It is called by the client
  236. // goroutine.
  237. func (e *eventLoop) Event() event.Event {
  238. for {
  239. evt := <-e.events
  240. // Receiving a flushEvent indicates to the platform backend that
  241. // all previous events have been processed by the user program.
  242. if _, ok := evt.(flushEvent); ok {
  243. continue
  244. }
  245. return evt
  246. }
  247. }
  248. // Invalidate requests invalidation of the window. It is called by the client
  249. // goroutine.
  250. func (e *eventLoop) Invalidate() {
  251. select {
  252. case e.immediateInvalidates <- struct{}{}:
  253. // The event loop was waiting, no need for a wakeup.
  254. case e.invalidates <- struct{}{}:
  255. // The event loop is sleeping, wake it up.
  256. e.wakeup()
  257. default:
  258. // A redraw is pending.
  259. }
  260. }
  261. // Run f in the window loop thread. It is called by the client goroutine.
  262. func (e *eventLoop) Run(f func()) {
  263. e.driverFuncs <- f
  264. e.wakeup()
  265. }
  266. // FlushEvents delivers pending events to the client.
  267. func (e *eventLoop) FlushEvents() {
  268. if e.delivering {
  269. return
  270. }
  271. e.delivering = true
  272. defer func() { e.delivering = false }()
  273. for {
  274. evt, ok := e.win.nextEvent()
  275. if !ok {
  276. break
  277. }
  278. e.deliverEvent(evt)
  279. }
  280. }
  281. func (e *eventLoop) deliverEvent(evt event.Event) {
  282. var frames <-chan *op.Ops
  283. for {
  284. select {
  285. case f := <-e.driverFuncs:
  286. f()
  287. case frame := <-frames:
  288. // The client called FrameEvent.Frame.
  289. frames = nil
  290. e.win.ProcessFrame(frame, e.frameAck)
  291. case e.events <- evt:
  292. switch evt.(type) {
  293. case flushEvent, DestroyEvent:
  294. // DestroyEvents are not flushed.
  295. return
  296. case FrameEvent:
  297. frames = e.frames
  298. }
  299. evt = theFlushEvent
  300. case <-e.invalidates:
  301. e.win.Invalidate()
  302. case <-e.immediateInvalidates:
  303. e.win.Invalidate()
  304. }
  305. }
  306. }
  307. func (e *eventLoop) Wakeup() {
  308. for {
  309. select {
  310. case f := <-e.driverFuncs:
  311. f()
  312. case <-e.invalidates:
  313. e.win.Invalidate()
  314. case <-e.immediateInvalidates:
  315. e.win.Invalidate()
  316. default:
  317. return
  318. }
  319. }
  320. }
  321. func walkActions(actions system.Action, do func(system.Action)) {
  322. for a := system.Action(1); actions != 0; a <<= 1 {
  323. if actions&a != 0 {
  324. actions &^= a
  325. do(a)
  326. }
  327. }
  328. }
  329. func (wakeupEvent) ImplementsEvent() {}
  330. func (ConfigEvent) ImplementsEvent() {}