mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-03 00:16:15 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8e47316332 | |||
| 55ae5c5b84 | |||
| 86349775b7 | |||
| 4a1b4c2642 | |||
| c900d58fb3 | |||
| 74ccc9c2c7 | |||
| 3f671afea8 | |||
| 42357a29e0 | |||
| 8fb6d3da2b | |||
| 706940ff9b | |||
| 5542aac772 | |||
| 026d3f9daa |
@@ -80,6 +80,8 @@ tasks:
|
|||||||
unzip -q ndk.zip
|
unzip -q ndk.zip
|
||||||
rm ndk.zip
|
rm ndk.zip
|
||||||
mv android-ndk-* ndk-bundle
|
mv android-ndk-* ndk-bundle
|
||||||
|
# sdkmanager needs lots of file descriptors
|
||||||
|
ulimit -n 10000
|
||||||
yes|sdkmanager --licenses
|
yes|sdkmanager --licenses
|
||||||
sdkmanager "platforms;android-31" "build-tools;32.0.0"
|
sdkmanager "platforms;android-31" "build-tools;32.0.0"
|
||||||
- test_android: |
|
- test_android: |
|
||||||
|
|||||||
@@ -61,6 +61,10 @@ type FrameEvent struct {
|
|||||||
type ViewEvent interface {
|
type ViewEvent interface {
|
||||||
implementsViewEvent()
|
implementsViewEvent()
|
||||||
ImplementsEvent()
|
ImplementsEvent()
|
||||||
|
// Valid will return true when the ViewEvent does contains valid handles.
|
||||||
|
// If a window receives an invalid ViewEvent, it should deinitialize any
|
||||||
|
// state referring to handles from a previous ViewEvent.
|
||||||
|
Valid() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insets is the space taken up by
|
// Insets is the space taken up by
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@
|
|||||||
#include <OpenGL/OpenGL.h>
|
#include <OpenGL/OpenGL.h>
|
||||||
#include "_cgo_export.h"
|
#include "_cgo_export.h"
|
||||||
|
|
||||||
CALayer *gio_layerFactory(void) {
|
CALayer *gio_layerFactory(BOOL presentWithTrans) {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
return [CALayer layer];
|
return [CALayer layer];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -266,6 +266,7 @@ const (
|
|||||||
WM_MOUSEWHEEL = 0x020A
|
WM_MOUSEWHEEL = 0x020A
|
||||||
WM_MOUSEHWHEEL = 0x020E
|
WM_MOUSEHWHEEL = 0x020E
|
||||||
WM_NCACTIVATE = 0x0086
|
WM_NCACTIVATE = 0x0086
|
||||||
|
WM_NCLBUTTONDBLCLK = 0x00A3
|
||||||
WM_NCHITTEST = 0x0084
|
WM_NCHITTEST = 0x0084
|
||||||
WM_NCCALCSIZE = 0x0083
|
WM_NCCALCSIZE = 0x0083
|
||||||
WM_PAINT = 0x000F
|
WM_PAINT = 0x000F
|
||||||
|
|||||||
+2
-2
@@ -12,12 +12,12 @@ package app
|
|||||||
#import <QuartzCore/CAMetalLayer.h>
|
#import <QuartzCore/CAMetalLayer.h>
|
||||||
#include <CoreFoundation/CoreFoundation.h>
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
|
|
||||||
CALayer *gio_layerFactory(void) {
|
CALayer *gio_layerFactory(BOOL presentWithTrans) {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
CAMetalLayer *l = [CAMetalLayer layer];
|
CAMetalLayer *l = [CAMetalLayer layer];
|
||||||
l.autoresizingMask = kCALayerHeightSizable|kCALayerWidthSizable;
|
l.autoresizingMask = kCALayerHeightSizable|kCALayerWidthSizable;
|
||||||
l.needsDisplayOnBoundsChange = YES;
|
l.needsDisplayOnBoundsChange = YES;
|
||||||
l.presentsWithTransaction = YES;
|
l.presentsWithTransaction = presentWithTrans;
|
||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,19 +173,13 @@ type context interface {
|
|||||||
Unlock()
|
Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// basicDriver is the subset of [driver] that may be called even after
|
// driver is the interface for the platform implementation
|
||||||
// a window is destroyed.
|
// of a window.
|
||||||
type basicDriver interface {
|
type driver interface {
|
||||||
// Event blocks until an event is available and returns it.
|
// Event blocks until an event is available and returns it.
|
||||||
Event() event.Event
|
Event() event.Event
|
||||||
// Invalidate requests a FrameEvent.
|
// Invalidate requests a FrameEvent.
|
||||||
Invalidate()
|
Invalidate()
|
||||||
}
|
|
||||||
|
|
||||||
// driver is the interface for the platform implementation
|
|
||||||
// of a window.
|
|
||||||
type driver interface {
|
|
||||||
basicDriver
|
|
||||||
// SetAnimating sets the animation flag. When the window is animating,
|
// SetAnimating sets the animation flag. When the window is animating,
|
||||||
// FrameEvents are delivered as fast as the display can handle them.
|
// FrameEvents are delivered as fast as the display can handle them.
|
||||||
SetAnimating(anim bool)
|
SetAnimating(anim bool)
|
||||||
|
|||||||
@@ -1495,3 +1495,6 @@ func Java_org_gioui_Gio_scheduleMainFuncs(env *C.JNIEnv, cls C.jclass) {
|
|||||||
|
|
||||||
func (AndroidViewEvent) implementsViewEvent() {}
|
func (AndroidViewEvent) implementsViewEvent() {}
|
||||||
func (AndroidViewEvent) ImplementsEvent() {}
|
func (AndroidViewEvent) ImplementsEvent() {}
|
||||||
|
func (a AndroidViewEvent) Valid() bool {
|
||||||
|
return a != (AndroidViewEvent{})
|
||||||
|
}
|
||||||
|
|||||||
@@ -441,3 +441,6 @@ func gio_runMain() {
|
|||||||
|
|
||||||
func (UIKitViewEvent) implementsViewEvent() {}
|
func (UIKitViewEvent) implementsViewEvent() {}
|
||||||
func (UIKitViewEvent) ImplementsEvent() {}
|
func (UIKitViewEvent) ImplementsEvent() {}
|
||||||
|
func (u UIKitViewEvent) Valid() bool {
|
||||||
|
return u != (UIKitViewEvent{})
|
||||||
|
}
|
||||||
|
|||||||
@@ -822,3 +822,6 @@ func translateKey(k string) (key.Name, bool) {
|
|||||||
|
|
||||||
func (JSViewEvent) implementsViewEvent() {}
|
func (JSViewEvent) implementsViewEvent() {}
|
||||||
func (JSViewEvent) ImplementsEvent() {}
|
func (JSViewEvent) ImplementsEvent() {}
|
||||||
|
func (j JSViewEvent) Valid() bool {
|
||||||
|
return !(j.Element.IsNull() || j.Element.IsUndefined())
|
||||||
|
}
|
||||||
|
|||||||
+13
-4
@@ -40,7 +40,7 @@ import (
|
|||||||
#define MOUSE_SCROLL 4
|
#define MOUSE_SCROLL 4
|
||||||
|
|
||||||
__attribute__ ((visibility ("hidden"))) void gio_main(void);
|
__attribute__ ((visibility ("hidden"))) void gio_main(void);
|
||||||
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createView(void);
|
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createView(int presentWithTrans);
|
||||||
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createWindow(CFTypeRef viewRef, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight);
|
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createWindow(CFTypeRef viewRef, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight);
|
||||||
__attribute__ ((visibility ("hidden"))) void gio_viewSetHandle(CFTypeRef viewRef, uintptr_t handle);
|
__attribute__ ((visibility ("hidden"))) void gio_viewSetHandle(CFTypeRef viewRef, uintptr_t handle);
|
||||||
|
|
||||||
@@ -925,7 +925,9 @@ func newWindow(win *callbacks, options []Option) {
|
|||||||
w.loop = newEventLoop(w.w, w.wakeup)
|
w.loop = newEventLoop(w.w, w.wakeup)
|
||||||
win.SetDriver(w)
|
win.SetDriver(w)
|
||||||
res <- struct{}{}
|
res <- struct{}{}
|
||||||
if err := w.init(); err != nil {
|
var cnf Config
|
||||||
|
cnf.apply(unit.Metric{}, options)
|
||||||
|
if err := w.init(cnf.CustomRenderer); err != nil {
|
||||||
w.ProcessEvent(DestroyEvent{Err: err})
|
w.ProcessEvent(DestroyEvent{Err: err})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -946,8 +948,12 @@ func newWindow(win *callbacks, options []Option) {
|
|||||||
<-res
|
<-res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) init() error {
|
func (w *window) init(customRenderer bool) error {
|
||||||
view := C.gio_createView()
|
presentWithTrans := 1
|
||||||
|
if customRenderer {
|
||||||
|
presentWithTrans = 0
|
||||||
|
}
|
||||||
|
view := C.gio_createView(C.int(presentWithTrans))
|
||||||
if view == 0 {
|
if view == 0 {
|
||||||
return errors.New("newOSWindow: failed to create view")
|
return errors.New("newOSWindow: failed to create view")
|
||||||
}
|
}
|
||||||
@@ -1068,3 +1074,6 @@ func convertMods(mods C.NSUInteger) key.Modifiers {
|
|||||||
|
|
||||||
func (AppKitViewEvent) implementsViewEvent() {}
|
func (AppKitViewEvent) implementsViewEvent() {}
|
||||||
func (AppKitViewEvent) ImplementsEvent() {}
|
func (AppKitViewEvent) ImplementsEvent() {}
|
||||||
|
func (a AppKitViewEvent) Valid() bool {
|
||||||
|
return a != (AppKitViewEvent{})
|
||||||
|
}
|
||||||
|
|||||||
+5
-3
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
#include "_cgo_export.h"
|
#include "_cgo_export.h"
|
||||||
|
|
||||||
__attribute__ ((visibility ("hidden"))) CALayer *gio_layerFactory(void);
|
__attribute__ ((visibility ("hidden"))) CALayer *gio_layerFactory(BOOL presentWithTrans);
|
||||||
|
|
||||||
@interface GioAppDelegate : NSObject<NSApplicationDelegate>
|
@interface GioAppDelegate : NSObject<NSApplicationDelegate>
|
||||||
@end
|
@end
|
||||||
@@ -16,6 +16,7 @@ __attribute__ ((visibility ("hidden"))) CALayer *gio_layerFactory(void);
|
|||||||
|
|
||||||
@interface GioView : NSView <CALayerDelegate,NSTextInputClient>
|
@interface GioView : NSView <CALayerDelegate,NSTextInputClient>
|
||||||
@property uintptr_t handle;
|
@property uintptr_t handle;
|
||||||
|
@property BOOL presentWithTrans;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation GioWindowDelegate
|
@implementation GioWindowDelegate
|
||||||
@@ -88,7 +89,7 @@ static void handleMouse(GioView *view, NSEvent *event, int typ, CGFloat dx, CGFl
|
|||||||
gio_onDraw(self.handle);
|
gio_onDraw(self.handle);
|
||||||
}
|
}
|
||||||
- (CALayer *)makeBackingLayer {
|
- (CALayer *)makeBackingLayer {
|
||||||
CALayer *layer = gio_layerFactory();
|
CALayer *layer = gio_layerFactory(self.presentWithTrans);
|
||||||
layer.delegate = self;
|
layer.delegate = self;
|
||||||
return layer;
|
return layer;
|
||||||
}
|
}
|
||||||
@@ -392,10 +393,11 @@ CFTypeRef gio_createWindow(CFTypeRef viewRef, CGFloat width, CGFloat height, CGF
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CFTypeRef gio_createView(void) {
|
CFTypeRef gio_createView(int presentWithTrans) {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
NSRect frame = NSMakeRect(0, 0, 0, 0);
|
NSRect frame = NSMakeRect(0, 0, 0, 0);
|
||||||
GioView* view = [[GioView alloc] initWithFrame:frame];
|
GioView* view = [[GioView alloc] initWithFrame:frame];
|
||||||
|
view.presentWithTrans = presentWithTrans ? YES : NO;
|
||||||
view.wantsLayer = YES;
|
view.wantsLayer = YES;
|
||||||
view.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;
|
view.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;
|
||||||
|
|
||||||
|
|||||||
+6
-24
@@ -9,7 +9,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"gioui.org/io/event"
|
|
||||||
"gioui.org/io/pointer"
|
"gioui.org/io/pointer"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,6 +21,9 @@ type X11ViewEvent struct {
|
|||||||
|
|
||||||
func (X11ViewEvent) implementsViewEvent() {}
|
func (X11ViewEvent) implementsViewEvent() {}
|
||||||
func (X11ViewEvent) ImplementsEvent() {}
|
func (X11ViewEvent) ImplementsEvent() {}
|
||||||
|
func (x X11ViewEvent) Valid() bool {
|
||||||
|
return x != (X11ViewEvent{})
|
||||||
|
}
|
||||||
|
|
||||||
type WaylandViewEvent struct {
|
type WaylandViewEvent struct {
|
||||||
// Display is the *wl_display returned by wl_display_connect.
|
// Display is the *wl_display returned by wl_display_connect.
|
||||||
@@ -32,6 +34,9 @@ type WaylandViewEvent struct {
|
|||||||
|
|
||||||
func (WaylandViewEvent) implementsViewEvent() {}
|
func (WaylandViewEvent) implementsViewEvent() {}
|
||||||
func (WaylandViewEvent) ImplementsEvent() {}
|
func (WaylandViewEvent) ImplementsEvent() {}
|
||||||
|
func (w WaylandViewEvent) Valid() bool {
|
||||||
|
return w != (WaylandViewEvent{})
|
||||||
|
}
|
||||||
|
|
||||||
func osMain() {
|
func osMain() {
|
||||||
select {}
|
select {}
|
||||||
@@ -57,35 +62,12 @@ func newWindow(window *callbacks, options []Option) {
|
|||||||
errFirst = err
|
errFirst = err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
window.SetDriver(&dummyDriver{
|
|
||||||
win: window,
|
|
||||||
wakeups: make(chan event.Event, 1),
|
|
||||||
})
|
|
||||||
if errFirst == nil {
|
if errFirst == nil {
|
||||||
errFirst = errors.New("app: no window driver available")
|
errFirst = errors.New("app: no window driver available")
|
||||||
}
|
}
|
||||||
window.ProcessEvent(DestroyEvent{Err: errFirst})
|
window.ProcessEvent(DestroyEvent{Err: errFirst})
|
||||||
}
|
}
|
||||||
|
|
||||||
type dummyDriver struct {
|
|
||||||
win *callbacks
|
|
||||||
wakeups chan event.Event
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dummyDriver) Event() event.Event {
|
|
||||||
if e, ok := d.win.nextEvent(); ok {
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
return <-d.wakeups
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dummyDriver) Invalidate() {
|
|
||||||
select {
|
|
||||||
case d.wakeups <- wakeupEvent{}:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// xCursor contains mapping from pointer.Cursor to XCursor.
|
// xCursor contains mapping from pointer.Cursor to XCursor.
|
||||||
var xCursor = [...]string{
|
var xCursor = [...]string{
|
||||||
pointer.CursorDefault: "left_ptr",
|
pointer.CursorDefault: "left_ptr",
|
||||||
|
|||||||
+9
-17
@@ -217,10 +217,6 @@ type window struct {
|
|||||||
wakeups chan struct{}
|
wakeups chan struct{}
|
||||||
|
|
||||||
closing bool
|
closing bool
|
||||||
|
|
||||||
// invMu avoids the race between the destruction of disp and
|
|
||||||
// Invalidate waking it up.
|
|
||||||
invMu sync.Mutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type poller struct {
|
type poller struct {
|
||||||
@@ -1369,10 +1365,8 @@ func (w *window) close(err error) {
|
|||||||
w.ProcessEvent(WaylandViewEvent{})
|
w.ProcessEvent(WaylandViewEvent{})
|
||||||
w.ProcessEvent(DestroyEvent{Err: err})
|
w.ProcessEvent(DestroyEvent{Err: err})
|
||||||
w.destroy()
|
w.destroy()
|
||||||
w.invMu.Lock()
|
|
||||||
w.disp.destroy()
|
w.disp.destroy()
|
||||||
w.disp = nil
|
w.disp = nil
|
||||||
w.invMu.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) dispatch() {
|
func (w *window) dispatch() {
|
||||||
@@ -1416,11 +1410,7 @@ func (w *window) Invalidate() {
|
|||||||
default:
|
default:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.invMu.Lock()
|
w.disp.wakeup()
|
||||||
defer w.invMu.Unlock()
|
|
||||||
if w.disp != nil {
|
|
||||||
w.disp.wakeup()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) Run(f func()) {
|
func (w *window) Run(f func()) {
|
||||||
@@ -1643,6 +1633,14 @@ func (w *window) flushScroll() {
|
|||||||
if total == (f32.Point{}) {
|
if total == (f32.Point{}) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if w.scroll.steps == (image.Point{}) {
|
||||||
|
w.fling.xExtrapolation.SampleDelta(w.scroll.time, -w.scroll.dist.X)
|
||||||
|
w.fling.yExtrapolation.SampleDelta(w.scroll.time, -w.scroll.dist.Y)
|
||||||
|
}
|
||||||
|
// Zero scroll distance prior to calling ProcessEvent, otherwise we may recursively
|
||||||
|
// re-process the scroll distance.
|
||||||
|
w.scroll.dist = f32.Point{}
|
||||||
|
w.scroll.steps = image.Point{}
|
||||||
w.ProcessEvent(pointer.Event{
|
w.ProcessEvent(pointer.Event{
|
||||||
Kind: pointer.Scroll,
|
Kind: pointer.Scroll,
|
||||||
Source: pointer.Mouse,
|
Source: pointer.Mouse,
|
||||||
@@ -1652,12 +1650,6 @@ func (w *window) flushScroll() {
|
|||||||
Time: w.scroll.time,
|
Time: w.scroll.time,
|
||||||
Modifiers: w.disp.xkb.Modifiers(),
|
Modifiers: w.disp.xkb.Modifiers(),
|
||||||
})
|
})
|
||||||
if w.scroll.steps == (image.Point{}) {
|
|
||||||
w.fling.xExtrapolation.SampleDelta(w.scroll.time, -w.scroll.dist.X)
|
|
||||||
w.fling.yExtrapolation.SampleDelta(w.scroll.time, -w.scroll.dist.Y)
|
|
||||||
}
|
|
||||||
w.scroll.dist = f32.Point{}
|
|
||||||
w.scroll.steps = image.Point{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) onPointerMotion(x, y C.wl_fixed_t, t C.uint32_t) {
|
func (w *window) onPointerMotion(x, y C.wl_fixed_t, t C.uint32_t) {
|
||||||
|
|||||||
+9
-12
@@ -54,9 +54,6 @@ type window struct {
|
|||||||
borderSize image.Point
|
borderSize image.Point
|
||||||
config Config
|
config Config
|
||||||
loop *eventLoop
|
loop *eventLoop
|
||||||
|
|
||||||
// invMu avoids the race between destroying the window and Invalidate.
|
|
||||||
invMu sync.Mutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const _WM_WAKEUP = windows.WM_USER + iota
|
const _WM_WAKEUP = windows.WM_USER + iota
|
||||||
@@ -304,10 +301,8 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
|
|||||||
windows.ReleaseDC(w.hdc)
|
windows.ReleaseDC(w.hdc)
|
||||||
w.hdc = 0
|
w.hdc = 0
|
||||||
}
|
}
|
||||||
w.invMu.Lock()
|
|
||||||
// The system destroys the HWND for us.
|
// The system destroys the HWND for us.
|
||||||
w.hwnd = 0
|
w.hwnd = 0
|
||||||
w.invMu.Unlock()
|
|
||||||
windows.PostQuitMessage(0)
|
windows.PostQuitMessage(0)
|
||||||
case windows.WM_NCCALCSIZE:
|
case windows.WM_NCCALCSIZE:
|
||||||
if w.config.Decorated {
|
if w.config.Decorated {
|
||||||
@@ -331,6 +326,12 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
|
|||||||
mi := windows.GetMonitorInfo(w.hwnd)
|
mi := windows.GetMonitorInfo(w.hwnd)
|
||||||
szp.Rgrc[0] = mi.WorkArea
|
szp.Rgrc[0] = mi.WorkArea
|
||||||
return 0
|
return 0
|
||||||
|
case windows.WM_NCLBUTTONDBLCLK:
|
||||||
|
if !w.config.Decorated {
|
||||||
|
// Override Windows behaviour when we
|
||||||
|
// draw decorations.
|
||||||
|
return 0
|
||||||
|
}
|
||||||
case windows.WM_PAINT:
|
case windows.WM_PAINT:
|
||||||
w.draw(true)
|
w.draw(true)
|
||||||
case windows.WM_SIZE:
|
case windows.WM_SIZE:
|
||||||
@@ -620,13 +621,6 @@ func (w *window) Frame(frame *op.Ops) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) wakeup() {
|
func (w *window) wakeup() {
|
||||||
w.invMu.Lock()
|
|
||||||
defer w.invMu.Unlock()
|
|
||||||
if w.hwnd == 0 {
|
|
||||||
w.loop.Wakeup()
|
|
||||||
w.loop.FlushEvents()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := windows.PostMessage(w.hwnd, _WM_WAKEUP, 0, 0); err != nil {
|
if err := windows.PostMessage(w.hwnd, _WM_WAKEUP, 0, 0); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -993,3 +987,6 @@ func configForDPI(dpi int) unit.Metric {
|
|||||||
|
|
||||||
func (Win32ViewEvent) implementsViewEvent() {}
|
func (Win32ViewEvent) implementsViewEvent() {}
|
||||||
func (Win32ViewEvent) ImplementsEvent() {}
|
func (Win32ViewEvent) ImplementsEvent() {}
|
||||||
|
func (w Win32ViewEvent) Valid() bool {
|
||||||
|
return w != (Win32ViewEvent{})
|
||||||
|
}
|
||||||
|
|||||||
@@ -113,9 +113,6 @@ type x11Window struct {
|
|||||||
wakeups chan struct{}
|
wakeups chan struct{}
|
||||||
handler x11EventHandler
|
handler x11EventHandler
|
||||||
buf [100]byte
|
buf [100]byte
|
||||||
|
|
||||||
// invMy avoids the race between destroy and Invalidate.
|
|
||||||
invMu sync.Mutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -416,11 +413,6 @@ func (w *x11Window) Invalidate() {
|
|||||||
case w.wakeups <- struct{}{}:
|
case w.wakeups <- struct{}{}:
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
w.invMu.Lock()
|
|
||||||
defer w.invMu.Unlock()
|
|
||||||
if w.x == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if _, err := syscall.Write(w.notify.write, x11OneByte); err != nil && err != syscall.EAGAIN {
|
if _, err := syscall.Write(w.notify.write, x11OneByte); err != nil && err != syscall.EAGAIN {
|
||||||
panic(fmt.Errorf("failed to write to pipe: %v", err))
|
panic(fmt.Errorf("failed to write to pipe: %v", err))
|
||||||
}
|
}
|
||||||
@@ -509,8 +501,6 @@ func (w *x11Window) dispatch() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *x11Window) destroy() {
|
func (w *x11Window) destroy() {
|
||||||
w.invMu.Lock()
|
|
||||||
defer w.invMu.Unlock()
|
|
||||||
if w.notify.write != 0 {
|
if w.notify.write != 0 {
|
||||||
syscall.Close(w.notify.write)
|
syscall.Close(w.notify.write)
|
||||||
w.notify.write = 0
|
w.notify.write = 0
|
||||||
|
|||||||
+63
-22
@@ -7,8 +7,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
"reflect"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
@@ -41,7 +41,8 @@ type Option func(unit.Metric, *Config)
|
|||||||
//
|
//
|
||||||
// More than one Window is not supported on iOS, Android, WebAssembly.
|
// More than one Window is not supported on iOS, Android, WebAssembly.
|
||||||
type Window struct {
|
type Window struct {
|
||||||
initialOpts []Option
|
initialOpts []Option
|
||||||
|
initialActions []system.Action
|
||||||
|
|
||||||
ctx context
|
ctx context
|
||||||
gpu gpu.GPU
|
gpu gpu.GPU
|
||||||
@@ -88,8 +89,11 @@ type Window struct {
|
|||||||
}
|
}
|
||||||
imeState editorState
|
imeState editorState
|
||||||
driver driver
|
driver driver
|
||||||
// basic is the driver interface that is needed even after the window is gone.
|
|
||||||
basic basicDriver
|
// invMu protects mayInvalidate.
|
||||||
|
invMu sync.Mutex
|
||||||
|
mayInvalidate bool
|
||||||
|
|
||||||
// coalesced tracks the most recent events waiting to be delivered
|
// coalesced tracks the most recent events waiting to be delivered
|
||||||
// to the client.
|
// to the client.
|
||||||
coalesced eventSummary
|
coalesced eventSummary
|
||||||
@@ -103,11 +107,12 @@ type Window struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type eventSummary struct {
|
type eventSummary struct {
|
||||||
wakeup bool
|
wakeup bool
|
||||||
cfg *ConfigEvent
|
cfg *ConfigEvent
|
||||||
view *ViewEvent
|
view *ViewEvent
|
||||||
frame *frameEvent
|
frame *frameEvent
|
||||||
destroy *DestroyEvent
|
framePending bool
|
||||||
|
destroy *DestroyEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
type callbacks struct {
|
type callbacks struct {
|
||||||
@@ -214,6 +219,7 @@ func (w *Window) frame(frame *op.Ops, viewport image.Point) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *Window) processFrame(frame *op.Ops, ack chan<- struct{}) {
|
func (w *Window) processFrame(frame *op.Ops, ack chan<- struct{}) {
|
||||||
|
w.coalesced.framePending = false
|
||||||
wrapper := &w.decorations.Ops
|
wrapper := &w.decorations.Ops
|
||||||
off := op.Offset(w.lastFrame.off).Push(wrapper)
|
off := op.Offset(w.lastFrame.off).Push(wrapper)
|
||||||
ops.AddCall(&wrapper.Internal, &frame.Internal, ops.PC{}, ops.PCFor(&frame.Internal))
|
ops.AddCall(&wrapper.Internal, &frame.Internal, ops.PC{}, ops.PCFor(&frame.Internal))
|
||||||
@@ -271,8 +277,11 @@ func (w *Window) updateState() {
|
|||||||
//
|
//
|
||||||
// Invalidate is safe for concurrent use.
|
// Invalidate is safe for concurrent use.
|
||||||
func (w *Window) Invalidate() {
|
func (w *Window) Invalidate() {
|
||||||
if w.basic != nil {
|
w.invMu.Lock()
|
||||||
w.basic.Invalidate()
|
defer w.invMu.Unlock()
|
||||||
|
if w.mayInvalidate {
|
||||||
|
w.mayInvalidate = false
|
||||||
|
w.driver.Invalidate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,7 +291,7 @@ func (w *Window) Option(opts ...Option) {
|
|||||||
if len(opts) == 0 {
|
if len(opts) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if w.basic == nil {
|
if w.driver == nil {
|
||||||
w.initialOpts = append(w.initialOpts, opts...)
|
w.initialOpts = append(w.initialOpts, opts...)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -377,11 +386,11 @@ func (w *Window) setNextFrame(at time.Time) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *callbacks) SetDriver(d basicDriver) {
|
func (c *callbacks) SetDriver(d driver) {
|
||||||
c.w.basic = d
|
if d == nil {
|
||||||
if d, ok := d.(driver); ok {
|
panic("nil driver")
|
||||||
c.w.driver = d
|
|
||||||
}
|
}
|
||||||
|
c.w.driver = d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *callbacks) ProcessFrame(frame *op.Ops, ack chan<- struct{}) {
|
func (c *callbacks) ProcessFrame(frame *op.Ops, ack chan<- struct{}) {
|
||||||
@@ -548,10 +557,20 @@ func (c *callbacks) Invalidate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *callbacks) nextEvent() (event.Event, bool) {
|
func (c *callbacks) nextEvent() (event.Event, bool) {
|
||||||
s := &c.w.coalesced
|
return c.w.nextEvent()
|
||||||
// Every event counts as a wakeup.
|
}
|
||||||
defer func() { s.wakeup = false }()
|
|
||||||
|
func (w *Window) nextEvent() (event.Event, bool) {
|
||||||
|
s := &w.coalesced
|
||||||
|
defer func() {
|
||||||
|
// Every event counts as a wakeup.
|
||||||
|
s.wakeup = false
|
||||||
|
}()
|
||||||
switch {
|
switch {
|
||||||
|
case s.framePending:
|
||||||
|
// If the user didn't call FrameEvent.Event, process
|
||||||
|
// an empty frame.
|
||||||
|
w.processFrame(new(op.Ops), nil)
|
||||||
case s.view != nil:
|
case s.view != nil:
|
||||||
e := *s.view
|
e := *s.view
|
||||||
s.view = nil
|
s.view = nil
|
||||||
@@ -568,10 +587,14 @@ func (c *callbacks) nextEvent() (event.Event, bool) {
|
|||||||
case s.frame != nil:
|
case s.frame != nil:
|
||||||
e := *s.frame
|
e := *s.frame
|
||||||
s.frame = nil
|
s.frame = nil
|
||||||
|
s.framePending = true
|
||||||
return e.FrameEvent, true
|
return e.FrameEvent, true
|
||||||
case s.wakeup:
|
case s.wakeup:
|
||||||
return wakeupEvent{}, true
|
return wakeupEvent{}, true
|
||||||
}
|
}
|
||||||
|
w.invMu.Lock()
|
||||||
|
defer w.invMu.Unlock()
|
||||||
|
w.mayInvalidate = w.driver != nil
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -615,6 +638,9 @@ func (w *Window) processEvent(e event.Event) bool {
|
|||||||
w.coalesced.frame = &e2
|
w.coalesced.frame = &e2
|
||||||
case DestroyEvent:
|
case DestroyEvent:
|
||||||
w.destroyGPU()
|
w.destroyGPU()
|
||||||
|
w.invMu.Lock()
|
||||||
|
w.mayInvalidate = false
|
||||||
|
w.invMu.Unlock()
|
||||||
w.driver = nil
|
w.driver = nil
|
||||||
if q := w.timer.quit; q != nil {
|
if q := w.timer.quit; q != nil {
|
||||||
q <- struct{}{}
|
q <- struct{}{}
|
||||||
@@ -622,7 +648,7 @@ func (w *Window) processEvent(e event.Event) bool {
|
|||||||
}
|
}
|
||||||
w.coalesced.destroy = &e2
|
w.coalesced.destroy = &e2
|
||||||
case ViewEvent:
|
case ViewEvent:
|
||||||
if reflect.ValueOf(e2).IsZero() && w.gpu != nil {
|
if !e2.Valid() && w.gpu != nil {
|
||||||
w.ctx.Lock()
|
w.ctx.Lock()
|
||||||
w.gpu.Release()
|
w.gpu.Release()
|
||||||
w.gpu = nil
|
w.gpu = nil
|
||||||
@@ -686,10 +712,17 @@ func (w *Window) processEvent(e event.Event) bool {
|
|||||||
// [FrameEvent], or until [Invalidate] is called. The window is created
|
// [FrameEvent], or until [Invalidate] is called. The window is created
|
||||||
// and shown the first time Event is called.
|
// and shown the first time Event is called.
|
||||||
func (w *Window) Event() event.Event {
|
func (w *Window) Event() event.Event {
|
||||||
if w.basic == nil {
|
if w.driver == nil {
|
||||||
w.init()
|
w.init()
|
||||||
}
|
}
|
||||||
return w.basic.Event()
|
if w.driver == nil {
|
||||||
|
e, ok := w.nextEvent()
|
||||||
|
if !ok {
|
||||||
|
panic("window initializion failed without a DestroyEvent")
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
return w.driver.Event()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Window) init() {
|
func (w *Window) init() {
|
||||||
@@ -727,6 +760,10 @@ func (w *Window) init() {
|
|||||||
w.imeState.compose = key.Range{Start: -1, End: -1}
|
w.imeState.compose = key.Range{Start: -1, End: -1}
|
||||||
w.semantic.ids = make(map[input.SemanticID]input.SemanticNode)
|
w.semantic.ids = make(map[input.SemanticID]input.SemanticNode)
|
||||||
newWindow(&callbacks{w}, options)
|
newWindow(&callbacks{w}, options)
|
||||||
|
for _, acts := range w.initialActions {
|
||||||
|
w.Perform(acts)
|
||||||
|
}
|
||||||
|
w.initialActions = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Window) updateCursor() {
|
func (w *Window) updateCursor() {
|
||||||
@@ -826,6 +863,10 @@ func (w *Window) Perform(actions system.Action) {
|
|||||||
if acts == 0 {
|
if acts == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if w.driver == nil {
|
||||||
|
w.initialActions = append(w.initialActions, acts)
|
||||||
|
return
|
||||||
|
}
|
||||||
w.Run(func() {
|
w.Run(func() {
|
||||||
w.driver.Perform(actions)
|
w.driver.Perform(actions)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1086,6 +1086,33 @@ func TestPassCursor(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPartialEvent(t *testing.T) {
|
||||||
|
var ops op.Ops
|
||||||
|
var r Router
|
||||||
|
|
||||||
|
rect := clip.Rect(image.Rect(0, 0, 100, 100))
|
||||||
|
background := rect.Push(&ops)
|
||||||
|
event.Op(&ops, 1)
|
||||||
|
background.Pop()
|
||||||
|
|
||||||
|
overlayPass := pointer.PassOp{}.Push(&ops)
|
||||||
|
overlay := rect.Push(&ops)
|
||||||
|
event.Op(&ops, 2)
|
||||||
|
overlay.Pop()
|
||||||
|
overlayPass.Pop()
|
||||||
|
assertEventSequence(t, events(&r, -1, pointer.Filter{Target: 1, Kinds: pointer.Press}))
|
||||||
|
assertEventSequence(t, events(&r, -1, pointer.Filter{Target: 2, Kinds: pointer.Press}))
|
||||||
|
r.Frame(&ops)
|
||||||
|
r.Queue(pointer.Event{
|
||||||
|
Kind: pointer.Press,
|
||||||
|
})
|
||||||
|
assertEventSequence(t, events(&r, -1, pointer.Filter{Target: 1, Kinds: pointer.Press}, key.FocusFilter{Target: 1}),
|
||||||
|
key.FocusEvent{}, pointer.Event{Kind: pointer.Press, Source: pointer.Mouse, Priority: pointer.Shared})
|
||||||
|
r.Source().Execute(key.FocusCmd{Tag: 1})
|
||||||
|
assertEventSequence(t, events(&r, -1, pointer.Filter{Target: 2, Kinds: pointer.Press}),
|
||||||
|
pointer.Event{Kind: pointer.Press, Source: pointer.Mouse, Priority: pointer.Foremost})
|
||||||
|
}
|
||||||
|
|
||||||
// offer satisfies io.ReadCloser for use in data transfers.
|
// offer satisfies io.ReadCloser for use in data transfers.
|
||||||
type offer struct {
|
type offer struct {
|
||||||
data string
|
data string
|
||||||
|
|||||||
+26
-25
@@ -274,28 +274,29 @@ func (q *Router) Event(filters ...event.Filter) (event.Event, bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !q.deferring {
|
for i := range q.changes {
|
||||||
for i := range q.changes {
|
if q.deferring && i > 0 {
|
||||||
change := &q.changes[i]
|
break
|
||||||
for j, evt := range change.events {
|
}
|
||||||
match := false
|
change := &q.changes[i]
|
||||||
switch e := evt.event.(type) {
|
for j, evt := range change.events {
|
||||||
case key.Event:
|
match := false
|
||||||
match = q.key.scratchFilter.Matches(change.state.keyState.focus, e, false)
|
switch e := evt.event.(type) {
|
||||||
default:
|
case key.Event:
|
||||||
for _, tf := range q.scratchFilters {
|
match = q.key.scratchFilter.Matches(change.state.keyState.focus, e, false)
|
||||||
if evt.tag == tf.tag && tf.filter.Matches(evt.event) {
|
default:
|
||||||
match = true
|
for _, tf := range q.scratchFilters {
|
||||||
break
|
if evt.tag == tf.tag && tf.filter.Matches(evt.event) {
|
||||||
}
|
match = true
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if match {
|
}
|
||||||
change.events = append(change.events[:j], change.events[j+1:]...)
|
if match {
|
||||||
// Fast forward state to last matched.
|
change.events = append(change.events[:j], change.events[j+1:]...)
|
||||||
q.collapseState(i)
|
// Fast forward state to last matched.
|
||||||
return evt.event, true
|
q.collapseState(i)
|
||||||
}
|
return evt.event, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -313,15 +314,15 @@ func (q *Router) collapseState(idx int) {
|
|||||||
}
|
}
|
||||||
first := &q.changes[0]
|
first := &q.changes[0]
|
||||||
first.state = q.changes[idx].state
|
first.state = q.changes[idx].state
|
||||||
for i := 1; i <= idx; i++ {
|
for _, ch := range q.changes[1 : idx+1] {
|
||||||
first.events = append(first.events, q.changes[i].events...)
|
first.events = append(first.events, ch.events...)
|
||||||
}
|
}
|
||||||
q.changes = append(q.changes[:1], q.changes[idx+1:]...)
|
q.changes = append(q.changes[:1], q.changes[idx+1:]...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Frame replaces the declared handlers from the supplied
|
// Frame completes the current frame and starts a new with the
|
||||||
// operation list. The text input state, wakeup time and whether
|
// handlers from the frame argument. Remaining events are discarded,
|
||||||
// there are active profile handlers is also saved.
|
// unless they were deferred by a command.
|
||||||
func (q *Router) Frame(frame *op.Ops) {
|
func (q *Router) Frame(frame *op.Ops) {
|
||||||
var remaining []event.Event
|
var remaining []event.Event
|
||||||
if n := len(q.changes); n > 0 {
|
if n := len(q.changes); n > 0 {
|
||||||
|
|||||||
Reference in New Issue
Block a user