os_macos.go 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168
  1. // SPDX-License-Identifier: Unlicense OR MIT
  2. //go:build darwin && !ios
  3. // +build darwin,!ios
  4. package app
  5. import (
  6. "errors"
  7. "image"
  8. "io"
  9. "runtime"
  10. "runtime/cgo"
  11. "strings"
  12. "time"
  13. "unicode"
  14. "unicode/utf8"
  15. "gioui.org/internal/f32"
  16. "gioui.org/io/event"
  17. "gioui.org/io/key"
  18. "gioui.org/io/pointer"
  19. "gioui.org/io/system"
  20. "gioui.org/io/transfer"
  21. "gioui.org/op"
  22. "gioui.org/unit"
  23. _ "gioui.org/internal/cocoainit"
  24. )
  25. /*
  26. #cgo CFLAGS: -Werror -Wno-deprecated-declarations -fobjc-arc -x objective-c
  27. #cgo LDFLAGS: -framework AppKit -framework QuartzCore
  28. #include <AppKit/AppKit.h>
  29. #define MOUSE_MOVE 1
  30. #define MOUSE_UP 2
  31. #define MOUSE_DOWN 3
  32. #define MOUSE_SCROLL 4
  33. __attribute__ ((visibility ("hidden"))) void gio_main(void);
  34. __attribute__ ((visibility ("hidden"))) CFTypeRef gio_createView(int presentWithTrans);
  35. __attribute__ ((visibility ("hidden"))) CFTypeRef gio_createWindow(CFTypeRef viewRef, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight);
  36. __attribute__ ((visibility ("hidden"))) void gio_viewSetHandle(CFTypeRef viewRef, uintptr_t handle);
  37. static void writeClipboard(CFTypeRef str) {
  38. @autoreleasepool {
  39. NSString *s = (__bridge NSString *)str;
  40. NSPasteboard *p = NSPasteboard.generalPasteboard;
  41. [p declareTypes:@[NSPasteboardTypeString] owner:nil];
  42. [p setString:s forType:NSPasteboardTypeString];
  43. }
  44. }
  45. static CFTypeRef readClipboard(void) {
  46. @autoreleasepool {
  47. NSPasteboard *p = NSPasteboard.generalPasteboard;
  48. NSString *content = [p stringForType:NSPasteboardTypeString];
  49. return (__bridge_retained CFTypeRef)content;
  50. }
  51. }
  52. static CGFloat viewHeight(CFTypeRef viewRef) {
  53. @autoreleasepool {
  54. NSView *view = (__bridge NSView *)viewRef;
  55. return [view bounds].size.height;
  56. }
  57. }
  58. static CGFloat viewWidth(CFTypeRef viewRef) {
  59. @autoreleasepool {
  60. NSView *view = (__bridge NSView *)viewRef;
  61. return [view bounds].size.width;
  62. }
  63. }
  64. static CGFloat getScreenBackingScale(void) {
  65. @autoreleasepool {
  66. return [NSScreen.mainScreen backingScaleFactor];
  67. }
  68. }
  69. static CGFloat getViewBackingScale(CFTypeRef viewRef) {
  70. @autoreleasepool {
  71. NSView *view = (__bridge NSView *)viewRef;
  72. return [view.window backingScaleFactor];
  73. }
  74. }
  75. static void setNeedsDisplay(CFTypeRef viewRef) {
  76. @autoreleasepool {
  77. NSView *view = (__bridge NSView *)viewRef;
  78. [view setNeedsDisplay:YES];
  79. }
  80. }
  81. static NSPoint cascadeTopLeftFromPoint(CFTypeRef windowRef, NSPoint topLeft) {
  82. @autoreleasepool {
  83. NSWindow *window = (__bridge NSWindow *)windowRef;
  84. return [window cascadeTopLeftFromPoint:topLeft];
  85. }
  86. }
  87. static void makeKeyAndOrderFront(CFTypeRef windowRef) {
  88. @autoreleasepool {
  89. NSWindow *window = (__bridge NSWindow *)windowRef;
  90. [window makeKeyAndOrderFront:nil];
  91. }
  92. }
  93. static void makeFirstResponder(CFTypeRef windowRef, CFTypeRef viewRef) {
  94. @autoreleasepool {
  95. NSWindow *window = (__bridge NSWindow *)windowRef;
  96. NSView *view = (__bridge NSView *)viewRef;
  97. [window makeFirstResponder:view];
  98. }
  99. }
  100. static void toggleFullScreen(CFTypeRef windowRef) {
  101. @autoreleasepool {
  102. NSWindow *window = (__bridge NSWindow *)windowRef;
  103. [window toggleFullScreen:nil];
  104. }
  105. }
  106. static NSWindowStyleMask getWindowStyleMask(CFTypeRef windowRef) {
  107. @autoreleasepool {
  108. NSWindow *window = (__bridge NSWindow *)windowRef;
  109. return [window styleMask];
  110. }
  111. }
  112. static void setWindowStyleMask(CFTypeRef windowRef, NSWindowStyleMask mask) {
  113. @autoreleasepool {
  114. NSWindow *window = (__bridge NSWindow *)windowRef;
  115. window.styleMask = mask;
  116. }
  117. }
  118. static void setWindowTitleVisibility(CFTypeRef windowRef, NSWindowTitleVisibility state) {
  119. @autoreleasepool {
  120. NSWindow *window = (__bridge NSWindow *)windowRef;
  121. window.titleVisibility = state;
  122. }
  123. }
  124. static void setWindowTitlebarAppearsTransparent(CFTypeRef windowRef, int transparent) {
  125. @autoreleasepool {
  126. NSWindow *window = (__bridge NSWindow *)windowRef;
  127. window.titlebarAppearsTransparent = (BOOL)transparent;
  128. }
  129. }
  130. static void setWindowStandardButtonHidden(CFTypeRef windowRef, NSWindowButton btn, int hide) {
  131. @autoreleasepool {
  132. NSWindow *window = (__bridge NSWindow *)windowRef;
  133. [window standardWindowButton:btn].hidden = (BOOL)hide;
  134. }
  135. }
  136. static void performWindowDragWithEvent(CFTypeRef windowRef, CFTypeRef evt) {
  137. @autoreleasepool {
  138. NSWindow *window = (__bridge NSWindow *)windowRef;
  139. [window performWindowDragWithEvent:(__bridge NSEvent*)evt];
  140. }
  141. }
  142. static void closeWindow(CFTypeRef windowRef) {
  143. @autoreleasepool {
  144. NSWindow* window = (__bridge NSWindow *)windowRef;
  145. [window performClose:nil];
  146. }
  147. }
  148. static void setSize(CFTypeRef windowRef, CGFloat width, CGFloat height) {
  149. @autoreleasepool {
  150. NSWindow* window = (__bridge NSWindow *)windowRef;
  151. NSSize size = NSMakeSize(width, height);
  152. [window setContentSize:size];
  153. }
  154. }
  155. static void setMinSize(CFTypeRef windowRef, CGFloat width, CGFloat height) {
  156. @autoreleasepool {
  157. NSWindow* window = (__bridge NSWindow *)windowRef;
  158. window.contentMinSize = NSMakeSize(width, height);
  159. }
  160. }
  161. static void setMaxSize(CFTypeRef windowRef, CGFloat width, CGFloat height) {
  162. @autoreleasepool {
  163. NSWindow* window = (__bridge NSWindow *)windowRef;
  164. window.contentMaxSize = NSMakeSize(width, height);
  165. }
  166. }
  167. static void setScreenFrame(CFTypeRef windowRef, CGFloat x, CGFloat y, CGFloat w, CGFloat h) {
  168. @autoreleasepool {
  169. NSWindow* window = (__bridge NSWindow *)windowRef;
  170. NSRect r = NSMakeRect(x, y, w, h);
  171. [window setFrame:r display:YES];
  172. }
  173. }
  174. static void resetLayerFrame(CFTypeRef viewRef) {
  175. @autoreleasepool {
  176. NSView* view = (__bridge NSView *)viewRef;
  177. NSRect r = view.frame;
  178. view.layer.frame = r;
  179. }
  180. }
  181. static void hideWindow(CFTypeRef windowRef) {
  182. @autoreleasepool {
  183. NSWindow* window = (__bridge NSWindow *)windowRef;
  184. [window miniaturize:window];
  185. }
  186. }
  187. static void unhideWindow(CFTypeRef windowRef) {
  188. @autoreleasepool {
  189. NSWindow* window = (__bridge NSWindow *)windowRef;
  190. [window deminiaturize:window];
  191. }
  192. }
  193. static NSRect getScreenFrame(CFTypeRef windowRef) {
  194. @autoreleasepool {
  195. NSWindow* window = (__bridge NSWindow *)windowRef;
  196. return [[window screen] frame];
  197. }
  198. }
  199. static void setTitle(CFTypeRef windowRef, CFTypeRef titleRef) {
  200. @autoreleasepool {
  201. NSWindow *window = (__bridge NSWindow *)windowRef;
  202. window.title = (__bridge NSString *)titleRef;
  203. }
  204. }
  205. static int isWindowZoomed(CFTypeRef windowRef) {
  206. @autoreleasepool {
  207. NSWindow *window = (__bridge NSWindow *)windowRef;
  208. return window.zoomed ? 1 : 0;
  209. }
  210. }
  211. static int isWindowMiniaturized(CFTypeRef windowRef) {
  212. @autoreleasepool {
  213. NSWindow *window = (__bridge NSWindow *)windowRef;
  214. return window.miniaturized ? 1 : 0;
  215. }
  216. }
  217. static void zoomWindow(CFTypeRef windowRef) {
  218. @autoreleasepool {
  219. NSWindow *window = (__bridge NSWindow *)windowRef;
  220. [window zoom:nil];
  221. }
  222. }
  223. static CFTypeRef layerForView(CFTypeRef viewRef) {
  224. @autoreleasepool {
  225. NSView *view = (__bridge NSView *)viewRef;
  226. return (__bridge CFTypeRef)view.layer;
  227. }
  228. }
  229. static CFTypeRef windowForView(CFTypeRef viewRef) {
  230. @autoreleasepool {
  231. NSView *view = (__bridge NSView *)viewRef;
  232. return (__bridge CFTypeRef)view.window;
  233. }
  234. }
  235. static void raiseWindow(CFTypeRef windowRef) {
  236. @autoreleasepool {
  237. NSRunningApplication *currentApp = [NSRunningApplication currentApplication];
  238. if (![currentApp isActive]) {
  239. [currentApp activateWithOptions:(NSApplicationActivateAllWindows | NSApplicationActivateIgnoringOtherApps)];
  240. }
  241. NSWindow* window = (__bridge NSWindow *)windowRef;
  242. [window makeKeyAndOrderFront:nil];
  243. }
  244. }
  245. static CFTypeRef createInputContext(CFTypeRef clientRef) {
  246. @autoreleasepool {
  247. id<NSTextInputClient> client = (__bridge id<NSTextInputClient>)clientRef;
  248. NSTextInputContext *ctx = [[NSTextInputContext alloc] initWithClient:client];
  249. return CFBridgingRetain(ctx);
  250. }
  251. }
  252. static void discardMarkedText(CFTypeRef viewRef) {
  253. @autoreleasepool {
  254. id<NSTextInputClient> view = (__bridge id<NSTextInputClient>)viewRef;
  255. NSTextInputContext *ctx = [NSTextInputContext currentInputContext];
  256. if (view == [ctx client]) {
  257. [ctx discardMarkedText];
  258. }
  259. }
  260. }
  261. static void invalidateCharacterCoordinates(CFTypeRef viewRef) {
  262. @autoreleasepool {
  263. id<NSTextInputClient> view = (__bridge id<NSTextInputClient>)viewRef;
  264. NSTextInputContext *ctx = [NSTextInputContext currentInputContext];
  265. if (view == [ctx client]) {
  266. [ctx invalidateCharacterCoordinates];
  267. }
  268. }
  269. }
  270. static void interpretKeyEvents(CFTypeRef viewRef, CFTypeRef eventRef) {
  271. @autoreleasepool {
  272. NSView *view = (__bridge NSView *)viewRef;
  273. NSEvent *event = (__bridge NSEvent *)eventRef;
  274. [view interpretKeyEvents:[NSArray arrayWithObject:event]];
  275. }
  276. }
  277. static int isMiniaturized(CFTypeRef windowRef) {
  278. @autoreleasepool {
  279. NSWindow *window = (__bridge NSWindow *)windowRef;
  280. return window.miniaturized ? 1 : 0;
  281. }
  282. }
  283. */
  284. import "C"
  285. func init() {
  286. // Darwin requires that UI operations happen on the main thread only.
  287. runtime.LockOSThread()
  288. }
  289. // AppKitViewEvent notifies the client of changes to the window AppKit handles.
  290. // The handles are retained until another AppKitViewEvent is sent.
  291. type AppKitViewEvent struct {
  292. // View is a CFTypeRef for the NSView for the window.
  293. View uintptr
  294. // Layer is a CFTypeRef of the CALayer of View.
  295. Layer uintptr
  296. }
  297. type window struct {
  298. view C.CFTypeRef
  299. w *callbacks
  300. anim bool
  301. displayLink *displayLink
  302. // redraw is a single entry channel for making sure only one
  303. // display link redraw request is in flight.
  304. redraw chan struct{}
  305. cursor pointer.Cursor
  306. pointerBtns pointer.Buttons
  307. loop *eventLoop
  308. lastMods C.NSUInteger
  309. scale float32
  310. config Config
  311. keysDown map[key.Name]struct{}
  312. // cmdKeys is for storing the current key event while
  313. // waiting for a doCommandBySelector.
  314. cmdKeys cmdKeys
  315. }
  316. type cmdKeys struct {
  317. eventStr string
  318. eventMods key.Modifiers
  319. }
  320. // launched is closed when applicationDidFinishLaunching is called.
  321. var launched = make(chan struct{})
  322. // nextTopLeft is the offset to use for the next window's call to
  323. // cascadeTopLeftFromPoint.
  324. var nextTopLeft C.NSPoint
  325. func windowFor(h C.uintptr_t) *window {
  326. return cgo.Handle(h).Value().(*window)
  327. }
  328. func (w *window) contextView() C.CFTypeRef {
  329. return w.view
  330. }
  331. func (w *window) ReadClipboard() {
  332. cstr := C.readClipboard()
  333. if cstr != 0 {
  334. defer C.CFRelease(cstr)
  335. }
  336. content := nsstringToString(cstr)
  337. w.ProcessEvent(transfer.DataEvent{
  338. Type: "application/text",
  339. Open: func() io.ReadCloser {
  340. return io.NopCloser(strings.NewReader(content))
  341. },
  342. })
  343. }
  344. func (w *window) WriteClipboard(mime string, s []byte) {
  345. cstr := stringToNSString(string(s))
  346. defer C.CFRelease(cstr)
  347. C.writeClipboard(cstr)
  348. }
  349. func (w *window) updateWindowMode() {
  350. w.scale = float32(C.getViewBackingScale(w.view))
  351. wf, hf := float32(C.viewWidth(w.view)), float32(C.viewHeight(w.view))
  352. w.config.Size = image.Point{
  353. X: int(wf*w.scale + .5),
  354. Y: int(hf*w.scale + .5),
  355. }
  356. w.config.Mode = Windowed
  357. window := C.windowForView(w.view)
  358. if window == 0 {
  359. return
  360. }
  361. style := int(C.getWindowStyleMask(C.windowForView(w.view)))
  362. switch {
  363. case style&C.NSWindowStyleMaskFullScreen != 0:
  364. w.config.Mode = Fullscreen
  365. case C.isWindowZoomed(window) != 0:
  366. w.config.Mode = Maximized
  367. }
  368. w.config.Decorated = style&C.NSWindowStyleMaskFullSizeContentView == 0
  369. }
  370. func (w *window) Configure(options []Option) {
  371. screenScale := float32(C.getScreenBackingScale())
  372. cfg := configFor(screenScale)
  373. cnf := w.config
  374. cnf.apply(cfg, options)
  375. window := C.windowForView(w.view)
  376. mask := C.getWindowStyleMask(window)
  377. fullscreen := mask&C.NSWindowStyleMaskFullScreen != 0
  378. switch cnf.Mode {
  379. case Fullscreen:
  380. if C.isWindowMiniaturized(window) != 0 {
  381. C.unhideWindow(window)
  382. }
  383. if !fullscreen {
  384. C.toggleFullScreen(window)
  385. }
  386. case Minimized:
  387. C.hideWindow(window)
  388. case Maximized:
  389. if C.isWindowMiniaturized(window) != 0 {
  390. C.unhideWindow(window)
  391. }
  392. if fullscreen {
  393. C.toggleFullScreen(window)
  394. }
  395. w.setTitle(cnf.Title)
  396. if C.isWindowZoomed(window) == 0 {
  397. C.zoomWindow(window)
  398. }
  399. case Windowed:
  400. if C.isWindowMiniaturized(window) != 0 {
  401. C.unhideWindow(window)
  402. }
  403. if fullscreen {
  404. C.toggleFullScreen(window)
  405. }
  406. w.setTitle(cnf.Title)
  407. w.config.Size = cnf.Size
  408. cnf.Size = cnf.Size.Div(int(screenScale))
  409. C.setSize(window, C.CGFloat(cnf.Size.X), C.CGFloat(cnf.Size.Y))
  410. w.config.MinSize = cnf.MinSize
  411. cnf.MinSize = cnf.MinSize.Div(int(screenScale))
  412. C.setMinSize(window, C.CGFloat(cnf.MinSize.X), C.CGFloat(cnf.MinSize.Y))
  413. w.config.MaxSize = cnf.MaxSize
  414. cnf.MaxSize = cnf.MaxSize.Div(int(screenScale))
  415. if cnf.MaxSize != (image.Point{}) {
  416. C.setMaxSize(window, C.CGFloat(cnf.MaxSize.X), C.CGFloat(cnf.MaxSize.Y))
  417. }
  418. if C.isWindowZoomed(window) != 0 {
  419. C.zoomWindow(window)
  420. }
  421. }
  422. style := C.NSWindowStyleMask(C.NSWindowStyleMaskTitled | C.NSWindowStyleMaskResizable | C.NSWindowStyleMaskMiniaturizable | C.NSWindowStyleMaskClosable)
  423. style = C.NSWindowStyleMaskFullSizeContentView
  424. mask &^= style
  425. barTrans := C.int(C.NO)
  426. titleVis := C.NSWindowTitleVisibility(C.NSWindowTitleVisible)
  427. if !cnf.Decorated {
  428. mask |= style
  429. barTrans = C.YES
  430. titleVis = C.NSWindowTitleHidden
  431. }
  432. C.setWindowTitlebarAppearsTransparent(window, barTrans)
  433. C.setWindowTitleVisibility(window, titleVis)
  434. C.setWindowStyleMask(window, mask)
  435. C.setWindowStandardButtonHidden(window, C.NSWindowCloseButton, barTrans)
  436. C.setWindowStandardButtonHidden(window, C.NSWindowMiniaturizeButton, barTrans)
  437. C.setWindowStandardButtonHidden(window, C.NSWindowZoomButton, barTrans)
  438. // When toggling the titlebar, the layer doesn't update its frame
  439. // until the next resize. Force it.
  440. C.resetLayerFrame(w.view)
  441. }
  442. func (w *window) setTitle(title string) {
  443. w.config.Title = title
  444. titleC := stringToNSString(title)
  445. defer C.CFRelease(titleC)
  446. C.setTitle(C.windowForView(w.view), titleC)
  447. }
  448. func (w *window) Perform(acts system.Action) {
  449. window := C.windowForView(w.view)
  450. walkActions(acts, func(a system.Action) {
  451. switch a {
  452. case system.ActionCenter:
  453. r := C.getScreenFrame(window) // the screen size of the window
  454. screenScale := float32(C.getScreenBackingScale())
  455. sz := w.config.Size.Div(int(screenScale))
  456. x := (int(r.size.width) - sz.X) / 2
  457. y := (int(r.size.height) - sz.Y) / 2
  458. C.setScreenFrame(window, C.CGFloat(x), C.CGFloat(y), C.CGFloat(sz.X), C.CGFloat(sz.Y))
  459. case system.ActionRaise:
  460. C.raiseWindow(window)
  461. }
  462. })
  463. if acts&system.ActionClose != 0 {
  464. C.closeWindow(window)
  465. }
  466. }
  467. func (w *window) SetCursor(cursor pointer.Cursor) {
  468. w.cursor = windowSetCursor(w.cursor, cursor)
  469. }
  470. func (w *window) EditorStateChanged(old, new editorState) {
  471. if old.Selection.Range != new.Selection.Range || old.Snippet != new.Snippet {
  472. C.discardMarkedText(w.view)
  473. w.w.SetComposingRegion(key.Range{Start: -1, End: -1})
  474. }
  475. if old.Selection.Caret != new.Selection.Caret || old.Selection.Transform != new.Selection.Transform {
  476. C.invalidateCharacterCoordinates(w.view)
  477. }
  478. }
  479. func (w *window) ShowTextInput(show bool) {}
  480. func (w *window) SetInputHint(_ key.InputHint) {}
  481. func (w *window) SetAnimating(anim bool) {
  482. w.anim = anim
  483. window := C.windowForView(w.view)
  484. if w.anim && window != 0 && C.isMiniaturized(window) == 0 {
  485. w.displayLink.Start()
  486. } else {
  487. w.displayLink.Stop()
  488. }
  489. }
  490. func (w *window) runOnMain(f func()) {
  491. runOnMain(func() {
  492. // Make sure the view is still valid. The window might've been closed
  493. // during the switch to the main thread.
  494. if w.view != 0 {
  495. f()
  496. }
  497. })
  498. }
  499. //export gio_onKeys
  500. func gio_onKeys(h C.uintptr_t, event C.CFTypeRef, cstr C.CFTypeRef, ti C.double, mods C.NSUInteger, keyDown C.bool) {
  501. w := windowFor(h)
  502. if w.keysDown == nil {
  503. w.keysDown = make(map[key.Name]struct{})
  504. }
  505. str := nsstringToString(cstr)
  506. kmods := convertMods(mods)
  507. ks := key.Release
  508. if keyDown {
  509. ks = key.Press
  510. w.cmdKeys.eventStr = str
  511. w.cmdKeys.eventMods = kmods
  512. C.interpretKeyEvents(w.view, event)
  513. }
  514. for _, k := range str {
  515. if n, ok := convertKey(k); ok {
  516. ke := key.Event{
  517. Name: n,
  518. Modifiers: kmods,
  519. State: ks,
  520. }
  521. if keyDown {
  522. w.keysDown[ke.Name] = struct{}{}
  523. if _, isCmd := convertCommandKey(k); isCmd || kmods.Contain(key.ModCommand) {
  524. // doCommandBySelector already processed the event.
  525. return
  526. }
  527. } else {
  528. if _, pressed := w.keysDown[n]; !pressed {
  529. continue
  530. }
  531. delete(w.keysDown, n)
  532. }
  533. w.ProcessEvent(ke)
  534. }
  535. }
  536. }
  537. //export gio_onCommandBySelector
  538. func gio_onCommandBySelector(h C.uintptr_t) C.bool {
  539. w := windowFor(h)
  540. ev := w.cmdKeys
  541. w.cmdKeys = cmdKeys{}
  542. handled := false
  543. for _, k := range ev.eventStr {
  544. n, ok := convertCommandKey(k)
  545. if !ok && ev.eventMods.Contain(key.ModCommand) {
  546. n, ok = convertKey(k)
  547. }
  548. if !ok {
  549. continue
  550. }
  551. ke := key.Event{
  552. Name: n,
  553. Modifiers: ev.eventMods,
  554. State: key.Press,
  555. }
  556. handled = w.processEvent(ke) || handled
  557. }
  558. return C.bool(handled)
  559. }
  560. //export gio_onFlagsChanged
  561. func gio_onFlagsChanged(h C.uintptr_t, curMods C.NSUInteger) {
  562. w := windowFor(h)
  563. mods := []C.NSUInteger{C.NSControlKeyMask, C.NSAlternateKeyMask, C.NSShiftKeyMask, C.NSCommandKeyMask}
  564. keys := []key.Name{key.NameCtrl, key.NameAlt, key.NameShift, key.NameCommand}
  565. for i, mod := range mods {
  566. wasPressed := w.lastMods&mod != 0
  567. isPressed := curMods&mod != 0
  568. if wasPressed != isPressed {
  569. st := key.Release
  570. if isPressed {
  571. st = key.Press
  572. }
  573. w.ProcessEvent(key.Event{
  574. Name: keys[i],
  575. State: st,
  576. })
  577. }
  578. }
  579. w.lastMods = curMods
  580. }
  581. //export gio_onText
  582. func gio_onText(h C.uintptr_t, cstr C.CFTypeRef) {
  583. str := nsstringToString(cstr)
  584. w := windowFor(h)
  585. w.w.EditorInsert(str)
  586. }
  587. //export gio_onMouse
  588. func gio_onMouse(h C.uintptr_t, evt C.CFTypeRef, cdir C.int, cbtn C.NSInteger, x, y, dx, dy C.CGFloat, ti C.double, mods C.NSUInteger) {
  589. w := windowFor(h)
  590. t := time.Duration(float64(ti)*float64(time.Second) + .5)
  591. xf, yf := float32(x)*w.scale, float32(y)*w.scale
  592. dxf, dyf := float32(dx)*w.scale, float32(dy)*w.scale
  593. pos := f32.Point{X: xf, Y: yf}
  594. var btn pointer.Buttons
  595. switch cbtn {
  596. case 0:
  597. btn = pointer.ButtonPrimary
  598. case 1:
  599. btn = pointer.ButtonSecondary
  600. case 2:
  601. btn = pointer.ButtonTertiary
  602. }
  603. var typ pointer.Kind
  604. switch cdir {
  605. case C.MOUSE_MOVE:
  606. typ = pointer.Move
  607. case C.MOUSE_UP:
  608. typ = pointer.Release
  609. w.pointerBtns &^= btn
  610. case C.MOUSE_DOWN:
  611. typ = pointer.Press
  612. w.pointerBtns |= btn
  613. act, ok := w.w.ActionAt(pos)
  614. if ok && w.config.Mode != Fullscreen {
  615. switch act {
  616. case system.ActionMove:
  617. C.performWindowDragWithEvent(C.windowForView(w.view), evt)
  618. return
  619. }
  620. }
  621. case C.MOUSE_SCROLL:
  622. typ = pointer.Scroll
  623. default:
  624. panic("invalid direction")
  625. }
  626. w.ProcessEvent(pointer.Event{
  627. Kind: typ,
  628. Source: pointer.Mouse,
  629. Time: t,
  630. Buttons: w.pointerBtns,
  631. Position: pos,
  632. Scroll: f32.Point{X: dxf, Y: dyf},
  633. Modifiers: convertMods(mods),
  634. })
  635. }
  636. //export gio_onDraw
  637. func gio_onDraw(h C.uintptr_t) {
  638. w := windowFor(h)
  639. w.draw()
  640. }
  641. //export gio_onFocus
  642. func gio_onFocus(h C.uintptr_t, focus C.int) {
  643. w := windowFor(h)
  644. w.SetCursor(w.cursor)
  645. w.config.Focused = focus == 1
  646. w.ProcessEvent(ConfigEvent{Config: w.config})
  647. }
  648. //export gio_onChangeScreen
  649. func gio_onChangeScreen(h C.uintptr_t, did uint64) {
  650. w := windowFor(h)
  651. w.displayLink.SetDisplayID(did)
  652. C.setNeedsDisplay(w.view)
  653. }
  654. //export gio_hasMarkedText
  655. func gio_hasMarkedText(h C.uintptr_t) C.int {
  656. w := windowFor(h)
  657. state := w.w.EditorState()
  658. if state.compose.Start != -1 {
  659. return 1
  660. }
  661. return 0
  662. }
  663. //export gio_markedRange
  664. func gio_markedRange(h C.uintptr_t) C.NSRange {
  665. w := windowFor(h)
  666. state := w.w.EditorState()
  667. rng := state.compose
  668. start, end := rng.Start, rng.End
  669. if start == -1 {
  670. return C.NSMakeRange(C.NSNotFound, 0)
  671. }
  672. u16start := state.UTF16Index(start)
  673. return C.NSMakeRange(
  674. C.NSUInteger(u16start),
  675. C.NSUInteger(state.UTF16Index(end)-u16start),
  676. )
  677. }
  678. //export gio_selectedRange
  679. func gio_selectedRange(h C.uintptr_t) C.NSRange {
  680. w := windowFor(h)
  681. state := w.w.EditorState()
  682. rng := state.Selection
  683. start, end := rng.Start, rng.End
  684. if start > end {
  685. start, end = end, start
  686. }
  687. u16start := state.UTF16Index(start)
  688. return C.NSMakeRange(
  689. C.NSUInteger(u16start),
  690. C.NSUInteger(state.UTF16Index(end)-u16start),
  691. )
  692. }
  693. //export gio_unmarkText
  694. func gio_unmarkText(h C.uintptr_t) {
  695. w := windowFor(h)
  696. w.w.SetComposingRegion(key.Range{Start: -1, End: -1})
  697. }
  698. //export gio_setMarkedText
  699. func gio_setMarkedText(h C.uintptr_t, cstr C.CFTypeRef, selRange C.NSRange, replaceRange C.NSRange) {
  700. w := windowFor(h)
  701. str := nsstringToString(cstr)
  702. state := w.w.EditorState()
  703. rng := state.compose
  704. if rng.Start == -1 {
  705. rng = state.Selection.Range
  706. }
  707. if replaceRange.location != C.NSNotFound {
  708. // replaceRange is relative to marked (or selected) text.
  709. offset := state.UTF16Index(rng.Start)
  710. start := state.RunesIndex(int(replaceRange.location) + offset)
  711. end := state.RunesIndex(int(replaceRange.location+replaceRange.length) + offset)
  712. rng = key.Range{
  713. Start: start,
  714. End: end,
  715. }
  716. }
  717. w.w.EditorReplace(rng, str)
  718. comp := key.Range{
  719. Start: rng.Start,
  720. End: rng.Start + utf8.RuneCountInString(str),
  721. }
  722. w.w.SetComposingRegion(comp)
  723. sel := key.Range{Start: comp.End, End: comp.End}
  724. if selRange.location != C.NSNotFound {
  725. // selRange is relative to inserted text.
  726. offset := state.UTF16Index(rng.Start)
  727. start := state.RunesIndex(int(selRange.location) + offset)
  728. end := state.RunesIndex(int(selRange.location+selRange.length) + offset)
  729. sel = key.Range{
  730. Start: start,
  731. End: end,
  732. }
  733. }
  734. w.w.SetEditorSelection(sel)
  735. }
  736. //export gio_substringForProposedRange
  737. func gio_substringForProposedRange(h C.uintptr_t, crng C.NSRange, actual C.NSRangePointer) C.CFTypeRef {
  738. w := windowFor(h)
  739. state := w.w.EditorState()
  740. start, end := state.Snippet.Start, state.Snippet.End
  741. if start > end {
  742. start, end = end, start
  743. }
  744. rng := key.Range{
  745. Start: state.RunesIndex(int(crng.location)),
  746. End: state.RunesIndex(int(crng.location + crng.length)),
  747. }
  748. if rng.Start < start || end < rng.End {
  749. w.w.SetEditorSnippet(rng)
  750. }
  751. u16start := state.UTF16Index(start)
  752. actual.location = C.NSUInteger(u16start)
  753. actual.length = C.NSUInteger(state.UTF16Index(end) - u16start)
  754. return stringToNSString(state.Snippet.Text)
  755. }
  756. //export gio_insertText
  757. func gio_insertText(h C.uintptr_t, cstr C.CFTypeRef, crng C.NSRange) {
  758. w := windowFor(h)
  759. str := nsstringToString(cstr)
  760. // macOS IME in some cases calls insertText for command keys such as backspace
  761. // instead of doCommandBySelector.
  762. for _, r := range str {
  763. if _, ok := convertCommandKey(r); ok {
  764. w.w.SetComposingRegion(key.Range{Start: -1, End: -1})
  765. return
  766. }
  767. }
  768. state := w.w.EditorState()
  769. rng := state.compose
  770. if rng.Start == -1 {
  771. rng = state.Selection.Range
  772. }
  773. if crng.location != C.NSNotFound {
  774. rng = key.Range{
  775. Start: state.RunesIndex(int(crng.location)),
  776. End: state.RunesIndex(int(crng.location + crng.length)),
  777. }
  778. }
  779. w.w.EditorReplace(rng, str)
  780. w.w.SetComposingRegion(key.Range{Start: -1, End: -1})
  781. start := rng.Start
  782. if rng.End < start {
  783. start = rng.End
  784. }
  785. pos := start + utf8.RuneCountInString(str)
  786. w.w.SetEditorSelection(key.Range{Start: pos, End: pos})
  787. }
  788. //export gio_characterIndexForPoint
  789. func gio_characterIndexForPoint(h C.uintptr_t, p C.NSPoint) C.NSUInteger {
  790. return C.NSNotFound
  791. }
  792. //export gio_firstRectForCharacterRange
  793. func gio_firstRectForCharacterRange(h C.uintptr_t, crng C.NSRange, actual C.NSRangePointer) C.NSRect {
  794. w := windowFor(h)
  795. state := w.w.EditorState()
  796. sel := state.Selection
  797. u16start := state.UTF16Index(sel.Start)
  798. actual.location = C.NSUInteger(u16start)
  799. actual.length = 0
  800. // Transform to NSView local coordinates (lower left origin, undo backing scale).
  801. scale := 1. / float32(C.getViewBackingScale(w.view))
  802. height := float32(C.viewHeight(w.view))
  803. local := f32.Affine2D{}.Scale(f32.Pt(0, 0), f32.Pt(scale, -scale)).Offset(f32.Pt(0, height))
  804. t := local.Mul(sel.Transform)
  805. bounds := f32.Rectangle{
  806. Min: t.Transform(sel.Pos.Sub(f32.Pt(0, sel.Ascent))),
  807. Max: t.Transform(sel.Pos.Add(f32.Pt(0, sel.Descent))),
  808. }.Canon()
  809. sz := bounds.Size()
  810. return C.NSMakeRect(
  811. C.CGFloat(bounds.Min.X), C.CGFloat(bounds.Min.Y),
  812. C.CGFloat(sz.X), C.CGFloat(sz.Y),
  813. )
  814. }
  815. func (w *window) draw() {
  816. cnf := w.config
  817. w.updateWindowMode()
  818. if w.config != cnf {
  819. w.ProcessEvent(ConfigEvent{Config: w.config})
  820. }
  821. select {
  822. case <-w.redraw:
  823. default:
  824. }
  825. if w.anim {
  826. w.SetAnimating(w.anim)
  827. }
  828. sz := w.config.Size
  829. if sz.X == 0 || sz.Y == 0 {
  830. return
  831. }
  832. cfg := configFor(w.scale)
  833. w.ProcessEvent(frameEvent{
  834. FrameEvent: FrameEvent{
  835. Now: time.Now(),
  836. Size: sz,
  837. Metric: cfg,
  838. },
  839. Sync: true,
  840. })
  841. }
  842. func (w *window) ProcessEvent(e event.Event) {
  843. w.processEvent(e)
  844. }
  845. func (w *window) processEvent(e event.Event) bool {
  846. handled := w.w.ProcessEvent(e)
  847. w.loop.FlushEvents()
  848. return handled
  849. }
  850. func (w *window) Event() event.Event {
  851. return w.loop.Event()
  852. }
  853. func (w *window) Invalidate() {
  854. w.loop.Invalidate()
  855. }
  856. func (w *window) Run(f func()) {
  857. w.loop.Run(f)
  858. }
  859. func (w *window) Frame(frame *op.Ops) {
  860. w.loop.Frame(frame)
  861. }
  862. func configFor(scale float32) unit.Metric {
  863. return unit.Metric{
  864. PxPerDp: scale,
  865. PxPerSp: scale,
  866. }
  867. }
  868. //export gio_onAttached
  869. func gio_onAttached(h C.uintptr_t, attached C.int) {
  870. w := windowFor(h)
  871. if attached != 0 {
  872. layer := C.layerForView(w.view)
  873. w.ProcessEvent(AppKitViewEvent{View: uintptr(w.view), Layer: uintptr(layer)})
  874. } else {
  875. w.ProcessEvent(AppKitViewEvent{})
  876. w.SetAnimating(w.anim)
  877. }
  878. }
  879. //export gio_onDestroy
  880. func gio_onDestroy(h C.uintptr_t) {
  881. w := windowFor(h)
  882. w.ProcessEvent(DestroyEvent{})
  883. w.displayLink.Close()
  884. w.displayLink = nil
  885. cgo.Handle(h).Delete()
  886. w.view = 0
  887. }
  888. //export gio_onFinishLaunching
  889. func gio_onFinishLaunching() {
  890. close(launched)
  891. }
  892. func newWindow(win *callbacks, options []Option) {
  893. <-launched
  894. res := make(chan struct{})
  895. runOnMain(func() {
  896. w := &window{
  897. redraw: make(chan struct{}, 1),
  898. w: win,
  899. }
  900. w.loop = newEventLoop(w.w, w.wakeup)
  901. win.SetDriver(w)
  902. res <- struct{}{}
  903. var cnf Config
  904. cnf.apply(unit.Metric{}, options)
  905. if err := w.init(cnf.CustomRenderer); err != nil {
  906. w.ProcessEvent(DestroyEvent{Err: err})
  907. return
  908. }
  909. window := C.gio_createWindow(w.view, C.CGFloat(cnf.Size.X), C.CGFloat(cnf.Size.Y), 0, 0, 0, 0)
  910. // Release our reference now that the NSWindow has it.
  911. C.CFRelease(w.view)
  912. w.Configure(options)
  913. if nextTopLeft.x == 0 && nextTopLeft.y == 0 {
  914. // cascadeTopLeftFromPoint treats (0, 0) as a no-op,
  915. // and just returns the offset we need for the first window.
  916. nextTopLeft = C.cascadeTopLeftFromPoint(window, nextTopLeft)
  917. }
  918. nextTopLeft = C.cascadeTopLeftFromPoint(window, nextTopLeft)
  919. C.makeFirstResponder(window, w.view)
  920. // makeKeyAndOrderFront assumes ownership of our window reference.
  921. C.makeKeyAndOrderFront(window)
  922. })
  923. <-res
  924. }
  925. func (w *window) init(customRenderer bool) error {
  926. presentWithTrans := 1
  927. if customRenderer {
  928. presentWithTrans = 0
  929. }
  930. view := C.gio_createView(C.int(presentWithTrans))
  931. if view == 0 {
  932. return errors.New("newOSWindow: failed to create view")
  933. }
  934. scale := float32(C.getViewBackingScale(view))
  935. w.scale = scale
  936. dl, err := newDisplayLink(func() {
  937. select {
  938. case w.redraw <- struct{}{}:
  939. default:
  940. return
  941. }
  942. w.runOnMain(func() {
  943. C.setNeedsDisplay(w.view)
  944. })
  945. })
  946. w.displayLink = dl
  947. if err != nil {
  948. C.CFRelease(view)
  949. return err
  950. }
  951. C.gio_viewSetHandle(view, C.uintptr_t(cgo.NewHandle(w)))
  952. w.view = view
  953. return nil
  954. }
  955. func osMain() {
  956. if !isMainThread() {
  957. panic("app.Main must run on the main goroutine")
  958. }
  959. C.gio_main()
  960. }
  961. func convertCommandKey(k rune) (key.Name, bool) {
  962. var n key.Name
  963. switch k {
  964. case '\x1b': // ASCII escape.
  965. n = key.NameEscape
  966. case C.NSLeftArrowFunctionKey:
  967. n = key.NameLeftArrow
  968. case C.NSRightArrowFunctionKey:
  969. n = key.NameRightArrow
  970. case C.NSUpArrowFunctionKey:
  971. n = key.NameUpArrow
  972. case C.NSDownArrowFunctionKey:
  973. n = key.NameDownArrow
  974. case '\r':
  975. n = key.NameReturn
  976. case '\x03':
  977. n = key.NameEnter
  978. case C.NSHomeFunctionKey:
  979. n = key.NameHome
  980. case C.NSEndFunctionKey:
  981. n = key.NameEnd
  982. case '\x7f', '\b':
  983. n = key.NameDeleteBackward
  984. case C.NSDeleteFunctionKey:
  985. n = key.NameDeleteForward
  986. case '\t', 0x19:
  987. n = key.NameTab
  988. case C.NSPageUpFunctionKey:
  989. n = key.NamePageUp
  990. case C.NSPageDownFunctionKey:
  991. n = key.NamePageDown
  992. default:
  993. return "", false
  994. }
  995. return n, true
  996. }
  997. func convertKey(k rune) (key.Name, bool) {
  998. if n, ok := convertCommandKey(k); ok {
  999. return n, true
  1000. }
  1001. var n key.Name
  1002. switch k {
  1003. case C.NSF1FunctionKey:
  1004. n = key.NameF1
  1005. case C.NSF2FunctionKey:
  1006. n = key.NameF2
  1007. case C.NSF3FunctionKey:
  1008. n = key.NameF3
  1009. case C.NSF4FunctionKey:
  1010. n = key.NameF4
  1011. case C.NSF5FunctionKey:
  1012. n = key.NameF5
  1013. case C.NSF6FunctionKey:
  1014. n = key.NameF6
  1015. case C.NSF7FunctionKey:
  1016. n = key.NameF7
  1017. case C.NSF8FunctionKey:
  1018. n = key.NameF8
  1019. case C.NSF9FunctionKey:
  1020. n = key.NameF9
  1021. case C.NSF10FunctionKey:
  1022. n = key.NameF10
  1023. case C.NSF11FunctionKey:
  1024. n = key.NameF11
  1025. case C.NSF12FunctionKey:
  1026. n = key.NameF12
  1027. case 0x20:
  1028. n = key.NameSpace
  1029. default:
  1030. k = unicode.ToUpper(k)
  1031. if !unicode.IsPrint(k) {
  1032. return "", false
  1033. }
  1034. n = key.Name(k)
  1035. }
  1036. return n, true
  1037. }
  1038. func convertMods(mods C.NSUInteger) key.Modifiers {
  1039. var kmods key.Modifiers
  1040. if mods&C.NSAlternateKeyMask != 0 {
  1041. kmods |= key.ModAlt
  1042. }
  1043. if mods&C.NSControlKeyMask != 0 {
  1044. kmods |= key.ModCtrl
  1045. }
  1046. if mods&C.NSCommandKeyMask != 0 {
  1047. kmods |= key.ModCommand
  1048. }
  1049. if mods&C.NSShiftKeyMask != 0 {
  1050. kmods |= key.ModShift
  1051. }
  1052. return kmods
  1053. }
  1054. func (AppKitViewEvent) implementsViewEvent() {}
  1055. func (AppKitViewEvent) ImplementsEvent() {}
  1056. func (a AppKitViewEvent) Valid() bool {
  1057. return a != (AppKitViewEvent{})
  1058. }