2 Commits

Author SHA1 Message Date
Elias Naur 8e47316332 app: [Windows] suppress double-click behaviour for custom decorations
Fixes: https://todo.sr.ht/~eliasnaur/gio/600
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2024-07-15 13:01:41 +08:00
Chris Waldon 55ae5c5b84 app: [Wayland] prevent recursive scroll event processing
This commit zeroes the accumulated scroll distance on the window before invoking the
event delivery code, since the event delivery code is able to call back into the scroll
processing. Prior to this change, the callback could re-processing the scroll delta
while magnifying it by a factor of 10.

Updates: https://todo.sr.ht/~eliasnaur/gio/599
Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2024-07-08 10:12:04 -04:00
109 changed files with 3834 additions and 2376 deletions
+2 -2
View File
@@ -29,7 +29,7 @@ environment:
tasks: tasks:
- install_go: | - install_go: |
mkdir -p /home/build/sdk mkdir -p /home/build/sdk
curl -s https://dl.google.com/go/go1.24.2.linux-amd64.tar.gz | tar -C /home/build/sdk -xzf - curl -s https://dl.google.com/go/go1.22.2.linux-amd64.tar.gz | tar -C /home/build/sdk -xzf -
- prepare_toolchain: | - prepare_toolchain: |
mkdir -p $APPLE_TOOLCHAIN_ROOT mkdir -p $APPLE_TOOLCHAIN_ROOT
cd $APPLE_TOOLCHAIN_ROOT cd $APPLE_TOOLCHAIN_ROOT
@@ -71,4 +71,4 @@ tasks:
CC=$APPLE_TOOLCHAIN_ROOT/tools/clang-macos GOOS=darwin CGO_ENABLED=1 go build ./... CC=$APPLE_TOOLCHAIN_ROOT/tools/clang-macos GOOS=darwin CGO_ENABLED=1 go build ./...
- test_ios: | - test_ios: |
cd gio cd gio
CGO_CFLAGS=-Wno-deprecated-module-dot-map CC=$APPLE_TOOLCHAIN_ROOT/tools/clang-ios GOOS=darwin GOARCH=arm64 CGO_ENABLED=1 go build -tags ios ./... CC=$APPLE_TOOLCHAIN_ROOT/tools/clang-ios GOOS=darwin GOARCH=arm64 CGO_ENABLED=1 go build -tags ios ./...
+2 -2
View File
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: Unlicense OR MIT # SPDX-License-Identifier: Unlicense OR MIT
image: freebsd/latest image: freebsd/13.x
packages: packages:
- libX11 - libX11
- libxkbcommon - libxkbcommon
@@ -16,7 +16,7 @@ environment:
tasks: tasks:
- install_go: | - install_go: |
mkdir -p /home/build/sdk mkdir -p /home/build/sdk
curl https://dl.google.com/go/go1.24.2.freebsd-amd64.tar.gz | tar -C /home/build/sdk -xzf - curl https://dl.google.com/go/go1.22.2.freebsd-amd64.tar.gz | tar -C /home/build/sdk -xzf -
- test_gio: | - test_gio: |
cd gio cd gio
go test ./... go test ./...
+1 -1
View File
@@ -40,7 +40,7 @@ secrets:
tasks: tasks:
- install_go: | - install_go: |
mkdir -p /home/build/sdk mkdir -p /home/build/sdk
curl -s https://dl.google.com/go/go1.24.2.linux-amd64.tar.gz | tar -C /home/build/sdk -xzf - curl -s https://dl.google.com/go/go1.22.2.linux-amd64.tar.gz | tar -C /home/build/sdk -xzf -
- check_gofmt: | - check_gofmt: |
cd gio cd gio
test -z "$(gofmt -s -l .)" test -z "$(gofmt -s -l .)"
+1 -1
View File
@@ -10,7 +10,7 @@ environment:
tasks: tasks:
- install_go: | - install_go: |
mkdir -p /home/build/sdk mkdir -p /home/build/sdk
curl https://dl.google.com/go/go1.24.2.src.tar.gz | tar -C /home/build/sdk -xzf - curl https://dl.google.com/go/go1.22.2.src.tar.gz | tar -C /home/build/sdk -xzf -
cd /home/build/sdk/go/src cd /home/build/sdk/go/src
./make.bash ./make.bash
- test_gio: | - test_gio: |
-4
View File
@@ -259,10 +259,6 @@ public final class GioView extends SurfaceView implements Choreographer.FrameCal
} }
private void setHighRefreshRate() { private void setHighRefreshRate() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
return;
}
Context context = getContext(); Context context = getContext();
Display display = context.getDisplay(); Display display = context.getDisplay();
Display.Mode[] supportedModes = display.getSupportedModes(); Display.Mode[] supportedModes = display.getSupportedModes();
+22 -19
View File
@@ -38,24 +38,7 @@ func init() {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &wlContext{Context: ctx, win: w}, nil
surf, width, height := w.surface()
if surf == nil {
return nil, errors.New("wayland: no surface")
}
eglWin := C.wl_egl_window_create(surf, C.int(width), C.int(height))
if eglWin == nil {
return nil, errors.New("wayland: wl_egl_window_create failed")
}
eglSurf := egl.NativeWindowType(uintptr(unsafe.Pointer(eglWin)))
if err := ctx.CreateSurface(eglSurf); err != nil {
return nil, err
}
// We're in charge of the frame callbacks, don't let eglSwapBuffers
// wait for callbacks that may never arrive.
ctx.EnableVSync(false)
return &wlContext{Context: ctx, win: w, eglWin: eglWin}, nil
} }
} }
@@ -71,11 +54,31 @@ func (c *wlContext) Release() {
} }
func (c *wlContext) Refresh() error { func (c *wlContext) Refresh() error {
c.Context.ReleaseSurface()
if c.eglWin != nil {
C.wl_egl_window_destroy(c.eglWin)
c.eglWin = nil
}
surf, width, height := c.win.surface() surf, width, height := c.win.surface()
if surf == nil { if surf == nil {
return errors.New("wayland: no surface") return errors.New("wayland: no surface")
} }
C.wl_egl_window_resize(c.eglWin, C.int(width), C.int(height), 0, 0) eglWin := C.wl_egl_window_create(surf, C.int(width), C.int(height))
if eglWin == nil {
return errors.New("wayland: wl_egl_window_create failed")
}
c.eglWin = eglWin
eglSurf := egl.NativeWindowType(uintptr(unsafe.Pointer(eglWin)))
if err := c.Context.CreateSurface(eglSurf); err != nil {
return err
}
if err := c.Context.MakeCurrent(); err != nil {
return err
}
defer c.Context.ReleaseCurrent()
// We're in charge of the frame callbacks, don't let eglSwapBuffers
// wait for callbacks that may never arrive.
c.Context.EnableVSync(false)
return nil return nil
} }
+1
View File
@@ -8,6 +8,7 @@ package app
import ( import (
"errors" "errors"
"runtime" "runtime"
"unsafe" "unsafe"
"gioui.org/gpu" "gioui.org/gpu"
-27
View File
@@ -1,11 +1,9 @@
// SPDX-License-Identifier: Unlicense OR MIT // SPDX-License-Identifier: Unlicense OR MIT
package app package app
import ( import (
"unicode" "unicode"
"unicode/utf16" "unicode/utf16"
"unicode/utf8"
"gioui.org/io/input" "gioui.org/io/input"
"gioui.org/io/key" "gioui.org/io/key"
@@ -118,28 +116,3 @@ func (e *editorState) RunesIndex(chars int) int {
// Assume runes after snippets are one UTF-16 character each. // Assume runes after snippets are one UTF-16 character each.
return runes + chars return runes + chars
} }
// areSnippetsConsistent reports whether the content of the old snippet is
// consistent with the content of the new.
func areSnippetsConsistent(old, new key.Snippet) bool {
// Compute the overlapping range.
r := old.Range
r.Start = max(r.Start, new.Start)
r.End = max(r.End, r.Start)
r.End = min(r.End, new.End)
return snippetSubstring(old, r) == snippetSubstring(new, r)
}
func snippetSubstring(s key.Snippet, r key.Range) string {
for r.Start > s.Start && r.Start < s.End {
_, n := utf8.DecodeRuneInString(s.Text)
s.Text = s.Text[n:]
s.Start++
}
for r.End < s.End && r.End > s.Start {
_, n := utf8.DecodeLastRuneInString(s.Text)
s.Text = s.Text[:len(s.Text)-n]
s.End--
}
return s.Text
}
-3
View File
@@ -6,7 +6,6 @@
package app package app
import ( import (
"gioui.org/f32"
"testing" "testing"
"unicode/utf8" "unicode/utf8"
@@ -41,7 +40,6 @@ func FuzzIME(f *testing.F) {
r.Frame(gtx.Ops) r.Frame(gtx.Ops)
var state editorState var state editorState
state.Selection.Transform = f32.AffineId()
const ( const (
cmdReplace = iota cmdReplace = iota
cmdSelect cmdSelect
@@ -141,7 +139,6 @@ func FuzzIME(f *testing.F) {
func TestEditorIndices(t *testing.T) { func TestEditorIndices(t *testing.T) {
var s editorState var s editorState
s.Selection.Transform = f32.AffineId()
const str = "Hello, 😀" const str = "Hello, 😀"
s.Snippet = key.Snippet{ s.Snippet = key.Snippet{
Text: str, Text: str,
+39 -141
View File
@@ -108,74 +108,6 @@ type MonitorInfo struct {
Flags uint32 Flags uint32
} }
type POINTER_INPUT_TYPE int32
const (
PT_POINTER POINTER_INPUT_TYPE = 1
PT_TOUCH POINTER_INPUT_TYPE = 2
PT_PEN POINTER_INPUT_TYPE = 3
PT_MOUSE POINTER_INPUT_TYPE = 4
PT_TOUCHPAD POINTER_INPUT_TYPE = 5
)
type POINTER_INFO_POINTER_FLAGS int32
const (
POINTER_FLAG_NEW POINTER_INFO_POINTER_FLAGS = 0x00000001
POINTER_FLAG_INRANGE POINTER_INFO_POINTER_FLAGS = 0x00000002
POINTER_FLAG_INCONTACT POINTER_INFO_POINTER_FLAGS = 0x00000004
POINTER_FLAG_FIRSTBUTTON POINTER_INFO_POINTER_FLAGS = 0x00000010
POINTER_FLAG_SECONDBUTTON POINTER_INFO_POINTER_FLAGS = 0x00000020
POINTER_FLAG_THIRDBUTTON POINTER_INFO_POINTER_FLAGS = 0x00000040
POINTER_FLAG_FOURTHBUTTON POINTER_INFO_POINTER_FLAGS = 0x00000080
POINTER_FLAG_FIFTHBUTTON POINTER_INFO_POINTER_FLAGS = 0x00000100
POINTER_FLAG_PRIMARY POINTER_INFO_POINTER_FLAGS = 0x00002000
POINTER_FLAG_CONFIDENCE POINTER_INFO_POINTER_FLAGS = 0x00004000
POINTER_FLAG_CANCELED POINTER_INFO_POINTER_FLAGS = 0x00008000
POINTER_FLAG_DOWN POINTER_INFO_POINTER_FLAGS = 0x00010000
POINTER_FLAG_UPDATE POINTER_INFO_POINTER_FLAGS = 0x00020000
POINTER_FLAG_UP POINTER_INFO_POINTER_FLAGS = 0x00040000
POINTER_FLAG_WHEEL POINTER_INFO_POINTER_FLAGS = 0x00080000
POINTER_FLAG_HWHEEL POINTER_INFO_POINTER_FLAGS = 0x00100000
POINTER_FLAG_CAPTURECHANGED POINTER_INFO_POINTER_FLAGS = 0x00200000
POINTER_FLAG_HASTRANSFORM POINTER_INFO_POINTER_FLAGS = 0x00400000
)
type POINTER_BUTTON_CHANGE_TYPE int32
const (
POINTER_CHANGE_NONE POINTER_BUTTON_CHANGE_TYPE = 0
POINTER_CHANGE_FIRSTBUTTON_DOWN POINTER_BUTTON_CHANGE_TYPE = 1
POINTER_CHANGE_FIRSTBUTTON_UP POINTER_BUTTON_CHANGE_TYPE = 2
POINTER_CHANGE_SECONDBUTTON_DOWN POINTER_BUTTON_CHANGE_TYPE = 3
POINTER_CHANGE_SECONDBUTTON_UP POINTER_BUTTON_CHANGE_TYPE = 4
POINTER_CHANGE_THIRDBUTTON_DOWN POINTER_BUTTON_CHANGE_TYPE = 5
POINTER_CHANGE_THIRDBUTTON_UP POINTER_BUTTON_CHANGE_TYPE = 6
POINTER_CHANGE_FOURTHBUTTON_DOWN POINTER_BUTTON_CHANGE_TYPE = 7
POINTER_CHANGE_FOURTHBUTTON_UP POINTER_BUTTON_CHANGE_TYPE = 8
POINTER_CHANGE_FIFTHBUTTON_DOWN POINTER_BUTTON_CHANGE_TYPE = 9
POINTER_CHANGE_FIFTHBUTTON_UP POINTER_BUTTON_CHANGE_TYPE = 10
)
type PointerInfo struct {
PointerType POINTER_INPUT_TYPE
PointerId uint32
FrameId uint32
PointerFlags POINTER_INFO_POINTER_FLAGS
SourceDevice syscall.Handle
HwndTarget syscall.Handle
PtPixelLocation Point
PtHimetricLocation Point
PtPixelLocationRaw Point
PtHimetricLocationRaw Point
DwTime uint32
HistoryCount uint32
InputData int32
DwKeyStates uint32
PerformanceCount uint64
ButtonChangeType POINTER_BUTTON_CHANGE_TYPE
}
const ( const (
TRUE = 1 TRUE = 1
@@ -312,51 +244,45 @@ const (
UNICODE_NOCHAR = 65535 UNICODE_NOCHAR = 65535
WM_CANCELMODE = 0x001F WM_CANCELMODE = 0x001F
WM_CHAR = 0x0102 WM_CHAR = 0x0102
WM_CLOSE = 0x0010 WM_CLOSE = 0x0010
WM_CREATE = 0x0001 WM_CREATE = 0x0001
WM_DPICHANGED = 0x02E0 WM_DPICHANGED = 0x02E0
WM_DESTROY = 0x0002 WM_DESTROY = 0x0002
WM_ERASEBKGND = 0x0014 WM_ERASEBKGND = 0x0014
WM_GETMINMAXINFO = 0x0024 WM_GETMINMAXINFO = 0x0024
WM_IME_COMPOSITION = 0x010F WM_IME_COMPOSITION = 0x010F
WM_IME_ENDCOMPOSITION = 0x010E WM_IME_ENDCOMPOSITION = 0x010E
WM_IME_STARTCOMPOSITION = 0x010D WM_IME_STARTCOMPOSITION = 0x010D
WM_KEYDOWN = 0x0100 WM_KEYDOWN = 0x0100
WM_KEYUP = 0x0101 WM_KEYUP = 0x0101
WM_KILLFOCUS = 0x0008 WM_KILLFOCUS = 0x0008
WM_LBUTTONDOWN = 0x0201 WM_LBUTTONDOWN = 0x0201
WM_LBUTTONUP = 0x0202 WM_LBUTTONUP = 0x0202
WM_MBUTTONDOWN = 0x0207 WM_MBUTTONDOWN = 0x0207
WM_MBUTTONUP = 0x0208 WM_MBUTTONUP = 0x0208
WM_MOUSEMOVE = 0x0200 WM_MOUSEMOVE = 0x0200
WM_MOUSEWHEEL = 0x020A WM_MOUSEWHEEL = 0x020A
WM_MOUSEHWHEEL = 0x020E WM_MOUSEHWHEEL = 0x020E
WM_NCACTIVATE = 0x0086 WM_NCACTIVATE = 0x0086
WM_NCHITTEST = 0x0084 WM_NCLBUTTONDBLCLK = 0x00A3
WM_NCCALCSIZE = 0x0083 WM_NCHITTEST = 0x0084
WM_PAINT = 0x000F WM_NCCALCSIZE = 0x0083
WM_POINTERCAPTURECHANGED = 0x024C WM_PAINT = 0x000F
WM_POINTERDOWN = 0x0246 WM_QUIT = 0x0012
WM_POINTERUP = 0x0247 WM_SETCURSOR = 0x0020
WM_POINTERUPDATE = 0x0245 WM_SETFOCUS = 0x0007
WM_POINTERWHEEL = 0x024E WM_SHOWWINDOW = 0x0018
WM_POINTERHWHEEL = 0x024F WM_SIZE = 0x0005
WM_QUIT = 0x0012 WM_SYSKEYDOWN = 0x0104
WM_RBUTTONDOWN = 0x0204 WM_SYSKEYUP = 0x0105
WM_RBUTTONUP = 0x0205 WM_RBUTTONDOWN = 0x0204
WM_SETCURSOR = 0x0020 WM_RBUTTONUP = 0x0205
WM_SETFOCUS = 0x0007 WM_TIMER = 0x0113
WM_SHOWWINDOW = 0x0018 WM_UNICHAR = 0x0109
WM_SIZE = 0x0005 WM_USER = 0x0400
WM_STYLECHANGED = 0x007D WM_WINDOWPOSCHANGED = 0x0047
WM_SYSKEYDOWN = 0x0104
WM_SYSKEYUP = 0x0105
WM_TIMER = 0x0113
WM_UNICHAR = 0x0109
WM_USER = 0x0400
WM_WINDOWPOSCHANGED = 0x0047
WS_CLIPCHILDREN = 0x02000000 WS_CLIPCHILDREN = 0x02000000
WS_CLIPSIBLINGS = 0x04000000 WS_CLIPSIBLINGS = 0x04000000
@@ -420,7 +346,6 @@ var (
_DestroyWindow = user32.NewProc("DestroyWindow") _DestroyWindow = user32.NewProc("DestroyWindow")
_DispatchMessage = user32.NewProc("DispatchMessageW") _DispatchMessage = user32.NewProc("DispatchMessageW")
_EmptyClipboard = user32.NewProc("EmptyClipboard") _EmptyClipboard = user32.NewProc("EmptyClipboard")
_EnableMouseInPointer = user32.NewProc("EnableMouseInPointer")
_GetWindowRect = user32.NewProc("GetWindowRect") _GetWindowRect = user32.NewProc("GetWindowRect")
_GetClientRect = user32.NewProc("GetClientRect") _GetClientRect = user32.NewProc("GetClientRect")
_GetClipboardData = user32.NewProc("GetClipboardData") _GetClipboardData = user32.NewProc("GetClipboardData")
@@ -430,7 +355,6 @@ var (
_GetMessage = user32.NewProc("GetMessageW") _GetMessage = user32.NewProc("GetMessageW")
_GetMessageTime = user32.NewProc("GetMessageTime") _GetMessageTime = user32.NewProc("GetMessageTime")
_GetMonitorInfo = user32.NewProc("GetMonitorInfoW") _GetMonitorInfo = user32.NewProc("GetMonitorInfoW")
_GetPointerInfo = user32.NewProc("GetPointerInfo")
_GetSystemMetrics = user32.NewProc("GetSystemMetrics") _GetSystemMetrics = user32.NewProc("GetSystemMetrics")
_GetWindowLong = user32.NewProc("GetWindowLongPtrW") _GetWindowLong = user32.NewProc("GetWindowLongPtrW")
_GetWindowLong32 = user32.NewProc("GetWindowLongW") _GetWindowLong32 = user32.NewProc("GetWindowLongW")
@@ -448,7 +372,6 @@ var (
_PostQuitMessage = user32.NewProc("PostQuitMessage") _PostQuitMessage = user32.NewProc("PostQuitMessage")
_ReleaseCapture = user32.NewProc("ReleaseCapture") _ReleaseCapture = user32.NewProc("ReleaseCapture")
_RegisterClassExW = user32.NewProc("RegisterClassExW") _RegisterClassExW = user32.NewProc("RegisterClassExW")
_RegisterTouchWindow = user32.NewProc("RegisterTouchWindow")
_ReleaseDC = user32.NewProc("ReleaseDC") _ReleaseDC = user32.NewProc("ReleaseDC")
_ScreenToClient = user32.NewProc("ScreenToClient") _ScreenToClient = user32.NewProc("ScreenToClient")
_ShowWindow = user32.NewProc("ShowWindow") _ShowWindow = user32.NewProc("ShowWindow")
@@ -522,31 +445,6 @@ func CreateWindowEx(dwExStyle uint32, lpClassName uint16, lpWindowName string, d
return syscall.Handle(hwnd), nil return syscall.Handle(hwnd), nil
} }
func GetPointerInfo(pointerId uint32) (PointerInfo, error) {
var info PointerInfo
r1, _, err := _GetPointerInfo.Call(uintptr(pointerId), uintptr(unsafe.Pointer(&info)))
if r1 == 0 {
return PointerInfo{}, fmt.Errorf("GetPointerInfo failed: %v", err)
}
return info, nil
}
func RegisterTouchWindow(hwnd syscall.Handle, flags uint32) error {
r1, _, err := _RegisterTouchWindow.Call(uintptr(hwnd), uintptr(flags))
if r1 == 0 {
return fmt.Errorf("RegisterTouchWindow failed: %v", err)
}
return nil
}
func EnableMouseInPointer(enable uint) error {
r1, _, err := _EnableMouseInPointer.Call(uintptr(enable))
if r1 == 0 {
return fmt.Errorf("EnableMouseInPointer failed: %v", err)
}
return nil
}
func DefWindowProc(hwnd syscall.Handle, msg uint32, wparam, lparam uintptr) uintptr { func DefWindowProc(hwnd syscall.Handle, msg uint32, wparam, lparam uintptr) uintptr {
r, _, _ := _DefWindowProc.Call(uintptr(hwnd), uintptr(msg), wparam, lparam) r, _, _ := _DefWindowProc.Call(uintptr(hwnd), uintptr(msg), wparam, lparam)
return r return r
+2 -3
View File
@@ -4,15 +4,14 @@ package app
import ( import (
"log" "log"
"syscall"
"unsafe" "unsafe"
syscall "golang.org/x/sys/windows"
) )
type logger struct{} type logger struct{}
var ( var (
kernel32 = syscall.NewLazySystemDLL("kernel32") kernel32 = syscall.NewLazyDLL("kernel32")
outputDebugStringW = kernel32.NewProc("OutputDebugStringW") outputDebugStringW = kernel32.NewProc("OutputDebugStringW")
debugView *logger debugView *logger
) )
+1 -1
View File
@@ -96,7 +96,7 @@ func newMtlContext(w *window) (*mtlContext, error) {
return nil, errors.New("metal: CAMetalLayer construction failed") return nil, errors.New("metal: CAMetalLayer construction failed")
} }
queue := C.newCommandQueue(dev) queue := C.newCommandQueue(dev)
if queue == 0 { if layer == 0 {
C.CFRelease(dev) C.CFRelease(dev)
C.CFRelease(layer) C.CFRelease(layer)
return nil, errors.New("metal: [MTLDevice newCommandQueue] failed") return nil, errors.New("metal: [MTLDevice newCommandQueue] failed")
+2
View File
@@ -195,6 +195,8 @@ type driver interface {
Configure([]Option) Configure([]Option)
// SetCursor updates the current cursor to name. // SetCursor updates the current cursor to name.
SetCursor(cursor pointer.Cursor) SetCursor(cursor pointer.Cursor)
// Wakeup wakes up the event loop and sends a WakeupEvent.
// Wakeup()
// Perform actions on the window. // Perform actions on the window.
Perform(system.Action) Perform(system.Action)
// EditorStateChanged notifies the driver that the editor state changed. // EditorStateChanged notifies the driver that the editor state changed.
+4 -4
View File
@@ -575,7 +575,10 @@ func Java_org_gioui_GioView_onFrameCallback(env *C.JNIEnv, class C.jclass, view
if !exist { if !exist {
return return
} }
w.draw(env, false) if w.visible && w.animating {
w.draw(env, false)
callVoidMethod(env, w.view, gioView.postFrameCallback)
}
} }
//export Java_org_gioui_GioView_onBack //export Java_org_gioui_GioView_onBack
@@ -879,9 +882,6 @@ func (w *window) draw(env *C.JNIEnv, sync bool) {
}, },
Sync: sync, Sync: sync,
}) })
if w.animating {
callVoidMethod(env, w.view, gioView.postFrameCallback)
}
a11yActive, err := callBooleanMethod(env, w.view, gioView.isA11yActive) a11yActive, err := callBooleanMethod(env, w.view, gioView.isA11yActive)
if err != nil { if err != nil {
panic(err) panic(err)
+17 -10
View File
@@ -5,7 +5,7 @@ package app
/* /*
#include <Foundation/Foundation.h> #include <Foundation/Foundation.h>
__attribute__ ((visibility ("hidden"))) void gio_runOnMain(uintptr_t h); __attribute__ ((visibility ("hidden"))) void gio_wakeupMainThread(void);
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createDisplayLink(void); __attribute__ ((visibility ("hidden"))) CFTypeRef gio_createDisplayLink(void);
__attribute__ ((visibility ("hidden"))) void gio_releaseDisplayLink(CFTypeRef dl); __attribute__ ((visibility ("hidden"))) void gio_releaseDisplayLink(CFTypeRef dl);
__attribute__ ((visibility ("hidden"))) int gio_startDisplayLink(CFTypeRef dl); __attribute__ ((visibility ("hidden"))) int gio_startDisplayLink(CFTypeRef dl);
@@ -40,10 +40,8 @@ static CFTypeRef newNSString(unichar *chars, NSUInteger length) {
} }
*/ */
import "C" import "C"
import ( import (
"errors" "errors"
"runtime/cgo"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
@@ -75,6 +73,8 @@ type displayLink struct {
// displayLinks maps CFTypeRefs to *displayLinks. // displayLinks maps CFTypeRefs to *displayLinks.
var displayLinks sync.Map var displayLinks sync.Map
var mainFuncs = make(chan func(), 1)
func isMainThread() bool { func isMainThread() bool {
return bool(C.isMainThread()) return bool(C.isMainThread())
} }
@@ -85,15 +85,22 @@ func runOnMain(f func()) {
f() f()
return return
} }
C.gio_runOnMain(C.uintptr_t(cgo.NewHandle(f))) go func() {
mainFuncs <- f
C.gio_wakeupMainThread()
}()
} }
//export gio_runFunc //export gio_dispatchMainFuncs
func gio_runFunc(h C.uintptr_t) { func gio_dispatchMainFuncs() {
handle := cgo.Handle(h) for {
defer handle.Delete() select {
f := handle.Value().(func()) case f := <-mainFuncs:
f() f()
default:
return
}
}
} }
// nsstringToString converts a NSString to a Go string. // nsstringToString converts a NSString to a Go string.
+2 -2
View File
@@ -4,8 +4,8 @@
#include "_cgo_export.h" #include "_cgo_export.h"
void gio_runOnMain(uintptr_t h) { void gio_wakeupMainThread(void) {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
gio_runFunc(h); gio_dispatchMainFuncs();
}); });
} }
+142 -235
View File
@@ -40,9 +40,8 @@ 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"))) void gio_init(void);
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createView(int presentWithTrans); __attribute__ ((visibility ("hidden"))) CFTypeRef gio_createView(int presentWithTrans);
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createWindow(CFTypeRef viewRef, CGFloat width, CGFloat height); __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);
static void writeClipboard(CFTypeRef str) { static void writeClipboard(CFTypeRef str) {
@@ -110,14 +109,6 @@ static void makeKeyAndOrderFront(CFTypeRef windowRef) {
} }
} }
static void makeFirstResponder(CFTypeRef windowRef, CFTypeRef viewRef) {
@autoreleasepool {
NSWindow *window = (__bridge NSWindow *)windowRef;
NSView *view = (__bridge NSView *)viewRef;
[window makeFirstResponder:view];
}
}
static void toggleFullScreen(CFTypeRef windowRef) { static void toggleFullScreen(CFTypeRef windowRef) {
@autoreleasepool { @autoreleasepool {
NSWindow *window = (__bridge NSWindow *)windowRef; NSWindow *window = (__bridge NSWindow *)windowRef;
@@ -193,7 +184,6 @@ static void setMaxSize(CFTypeRef windowRef, CGFloat width, CGFloat height) {
@autoreleasepool { @autoreleasepool {
NSWindow* window = (__bridge NSWindow *)windowRef; NSWindow* window = (__bridge NSWindow *)windowRef;
window.contentMaxSize = NSMakeSize(width, height); window.contentMaxSize = NSMakeSize(width, height);
window.maxFullScreenContentSize = NSMakeSize(width, height);
} }
} }
@@ -248,13 +238,6 @@ static int isWindowZoomed(CFTypeRef windowRef) {
} }
} }
static int isWindowMiniaturized(CFTypeRef windowRef) {
@autoreleasepool {
NSWindow *window = (__bridge NSWindow *)windowRef;
return window.miniaturized ? 1 : 0;
}
}
static void zoomWindow(CFTypeRef windowRef) { static void zoomWindow(CFTypeRef windowRef) {
@autoreleasepool { @autoreleasepool {
NSWindow *window = (__bridge NSWindow *)windowRef; NSWindow *window = (__bridge NSWindow *)windowRef;
@@ -314,29 +297,12 @@ static void invalidateCharacterCoordinates(CFTypeRef viewRef) {
} }
} }
} }
static void interpretKeyEvents(CFTypeRef viewRef, CFTypeRef eventRef) {
@autoreleasepool {
NSView *view = (__bridge NSView *)viewRef;
NSEvent *event = (__bridge NSEvent *)eventRef;
[view interpretKeyEvents:[NSArray arrayWithObject:event]];
}
}
static int isMiniaturized(CFTypeRef windowRef) {
@autoreleasepool {
NSWindow *window = (__bridge NSWindow *)windowRef;
return window.miniaturized ? 1 : 0;
}
}
*/ */
import "C" import "C"
func init() { func init() {
// Darwin requires that UI operations happen on the main thread only. // Darwin requires that UI operations happen on the main thread only.
runtime.LockOSThread() runtime.LockOSThread()
// Register launch finished listener.
C.gio_init()
} }
// AppKitViewEvent notifies the client of changes to the window AppKit handles. // AppKitViewEvent notifies the client of changes to the window AppKit handles.
@@ -352,6 +318,7 @@ type window struct {
view C.CFTypeRef view C.CFTypeRef
w *callbacks w *callbacks
anim bool anim bool
visible bool
displayLink *displayLink displayLink *displayLink
// redraw is a single entry channel for making sure only one // redraw is a single entry channel for making sure only one
// display link redraw request is in flight. // display link redraw request is in flight.
@@ -359,20 +326,9 @@ type window struct {
cursor pointer.Cursor cursor pointer.Cursor
pointerBtns pointer.Buttons pointerBtns pointer.Buttons
loop *eventLoop loop *eventLoop
lastMods C.NSUInteger
scale float32 scale float32
config Config config Config
keysDown map[key.Name]struct{}
// cmdKeys is for storing the current key event while
// waiting for a doCommandBySelector.
cmdKeys cmdKeys
}
type cmdKeys struct {
eventStr string
eventMods key.Modifiers
} }
// launched is closed when applicationDidFinishLaunching is called. // launched is closed when applicationDidFinishLaunching is called.
@@ -411,23 +367,11 @@ func (w *window) WriteClipboard(mime string, s []byte) {
} }
func (w *window) updateWindowMode() { func (w *window) updateWindowMode() {
w.scale = float32(C.getViewBackingScale(w.view))
wf, hf := float32(C.viewWidth(w.view)), float32(C.viewHeight(w.view))
w.config.Size = image.Point{
X: int(wf*w.scale + .5),
Y: int(hf*w.scale + .5),
}
w.config.Mode = Windowed
window := C.windowForView(w.view)
if window == 0 {
return
}
style := int(C.getWindowStyleMask(C.windowForView(w.view))) style := int(C.getWindowStyleMask(C.windowForView(w.view)))
switch { if style&C.NSWindowStyleMaskFullScreen != 0 {
case style&C.NSWindowStyleMaskFullScreen != 0:
w.config.Mode = Fullscreen w.config.Mode = Fullscreen
case C.isWindowZoomed(window) != 0: } else {
w.config.Mode = Maximized w.config.Mode = Windowed
} }
w.config.Decorated = style&C.NSWindowStyleMaskFullSizeContentView == 0 w.config.Decorated = style&C.NSWindowStyleMaskFullSizeContentView == 0
} }
@@ -435,82 +379,105 @@ func (w *window) updateWindowMode() {
func (w *window) Configure(options []Option) { func (w *window) Configure(options []Option) {
screenScale := float32(C.getScreenBackingScale()) screenScale := float32(C.getScreenBackingScale())
cfg := configFor(screenScale) cfg := configFor(screenScale)
prev := w.config
w.updateWindowMode()
cnf := w.config cnf := w.config
cnf.apply(cfg, options) cnf.apply(cfg, options)
window := C.windowForView(w.view) window := C.windowForView(w.view)
mask := C.getWindowStyleMask(window)
fullscreen := mask&C.NSWindowStyleMaskFullScreen != 0
switch cnf.Mode { switch cnf.Mode {
case Fullscreen: case Fullscreen:
if C.isWindowMiniaturized(window) != 0 { switch prev.Mode {
case Fullscreen:
case Minimized:
C.unhideWindow(window) C.unhideWindow(window)
} fallthrough
if !fullscreen { default:
w.config.Mode = Fullscreen
C.toggleFullScreen(window) C.toggleFullScreen(window)
} }
case Minimized: case Minimized:
C.hideWindow(window) switch prev.Mode {
case Minimized, Fullscreen:
default:
w.config.Mode = Minimized
C.hideWindow(window)
}
case Maximized: case Maximized:
if C.isWindowMiniaturized(window) != 0 { switch prev.Mode {
case Fullscreen:
case Minimized:
C.unhideWindow(window) C.unhideWindow(window)
} fallthrough
if fullscreen { default:
C.toggleFullScreen(window) w.config.Mode = Maximized
} w.setTitle(prev, cnf)
w.setTitle(cnf.Title) if C.isWindowZoomed(window) == 0 {
if C.isWindowZoomed(window) == 0 { C.zoomWindow(window)
C.zoomWindow(window) }
} }
case Windowed: case Windowed:
if C.isWindowMiniaturized(window) != 0 { switch prev.Mode {
C.unhideWindow(window) case Fullscreen:
}
if fullscreen {
C.toggleFullScreen(window) C.toggleFullScreen(window)
case Minimized:
C.unhideWindow(window)
case Maximized:
if C.isWindowZoomed(window) != 0 {
C.zoomWindow(window)
}
} }
w.setTitle(cnf.Title) w.config.Mode = Windowed
w.config.Size = cnf.Size w.setTitle(prev, cnf)
cnf.Size = cnf.Size.Div(int(screenScale)) if prev.Size != cnf.Size {
C.setSize(window, C.CGFloat(cnf.Size.X), C.CGFloat(cnf.Size.Y)) w.config.Size = cnf.Size
w.config.MinSize = cnf.MinSize cnf.Size = cnf.Size.Div(int(screenScale))
cnf.MinSize = cnf.MinSize.Div(int(screenScale)) C.setSize(window, C.CGFloat(cnf.Size.X), C.CGFloat(cnf.Size.Y))
C.setMinSize(window, C.CGFloat(cnf.MinSize.X), C.CGFloat(cnf.MinSize.Y)) }
w.config.MaxSize = cnf.MaxSize if prev.MinSize != cnf.MinSize {
cnf.MaxSize = cnf.MaxSize.Div(int(screenScale)) w.config.MinSize = cnf.MinSize
if cnf.MaxSize != (image.Point{}) { cnf.MinSize = cnf.MinSize.Div(int(screenScale))
C.setMinSize(window, C.CGFloat(cnf.MinSize.X), C.CGFloat(cnf.MinSize.Y))
}
if prev.MaxSize != cnf.MaxSize {
w.config.MaxSize = cnf.MaxSize
cnf.MaxSize = cnf.MaxSize.Div(int(screenScale))
C.setMaxSize(window, C.CGFloat(cnf.MaxSize.X), C.CGFloat(cnf.MaxSize.Y)) C.setMaxSize(window, C.CGFloat(cnf.MaxSize.X), C.CGFloat(cnf.MaxSize.Y))
} }
if C.isWindowZoomed(window) != 0 { }
C.zoomWindow(window) if cnf.Decorated != prev.Decorated {
w.config.Decorated = cnf.Decorated
mask := C.getWindowStyleMask(window)
style := C.NSWindowStyleMask(C.NSWindowStyleMaskTitled | C.NSWindowStyleMaskResizable | C.NSWindowStyleMaskMiniaturizable | C.NSWindowStyleMaskClosable)
style = C.NSWindowStyleMaskFullSizeContentView
mask &^= style
barTrans := C.int(C.NO)
titleVis := C.NSWindowTitleVisibility(C.NSWindowTitleVisible)
if !cnf.Decorated {
mask |= style
barTrans = C.YES
titleVis = C.NSWindowTitleHidden
} }
C.setWindowTitlebarAppearsTransparent(window, barTrans)
C.setWindowTitleVisibility(window, titleVis)
C.setWindowStyleMask(window, mask)
C.setWindowStandardButtonHidden(window, C.NSWindowCloseButton, barTrans)
C.setWindowStandardButtonHidden(window, C.NSWindowMiniaturizeButton, barTrans)
C.setWindowStandardButtonHidden(window, C.NSWindowZoomButton, barTrans)
// When toggling the titlebar, the layer doesn't update its frame
// until the next resize. Force it.
C.resetLayerFrame(w.view)
} }
style := C.NSWindowStyleMask(C.NSWindowStyleMaskTitled | C.NSWindowStyleMaskResizable | C.NSWindowStyleMaskMiniaturizable | C.NSWindowStyleMaskClosable) w.ProcessEvent(ConfigEvent{Config: w.config})
style = C.NSWindowStyleMaskFullSizeContentView
mask &^= style
barTrans := C.int(C.NO)
titleVis := C.NSWindowTitleVisibility(C.NSWindowTitleVisible)
if !cnf.Decorated {
mask |= style
barTrans = C.YES
titleVis = C.NSWindowTitleHidden
}
C.setWindowTitlebarAppearsTransparent(window, barTrans)
C.setWindowTitleVisibility(window, titleVis)
C.setWindowStyleMask(window, mask)
C.setWindowStandardButtonHidden(window, C.NSWindowCloseButton, barTrans)
C.setWindowStandardButtonHidden(window, C.NSWindowMiniaturizeButton, barTrans)
C.setWindowStandardButtonHidden(window, C.NSWindowZoomButton, barTrans)
// When toggling the titlebar, the layer doesn't update its frame
// until the next resize. Force it.
C.resetLayerFrame(w.view)
} }
func (w *window) setTitle(title string) { func (w *window) setTitle(prev, cnf Config) {
w.config.Title = title if prev.Title != cnf.Title {
titleC := stringToNSString(title) w.config.Title = cnf.Title
defer C.CFRelease(titleC) title := stringToNSString(cnf.Title)
C.setTitle(C.windowForView(w.view), titleC) defer C.CFRelease(title)
C.setTitle(C.windowForView(w.view), title)
}
} }
func (w *window) Perform(acts system.Action) { func (w *window) Perform(acts system.Action) {
@@ -538,7 +505,7 @@ func (w *window) SetCursor(cursor pointer.Cursor) {
} }
func (w *window) EditorStateChanged(old, new editorState) { func (w *window) EditorStateChanged(old, new editorState) {
if old.Selection.Range != new.Selection.Range || !areSnippetsConsistent(old.Snippet, new.Snippet) { if old.Selection.Range != new.Selection.Range || old.Snippet != new.Snippet {
C.discardMarkedText(w.view) C.discardMarkedText(w.view)
w.w.SetComposingRegion(key.Range{Start: -1, End: -1}) w.w.SetComposingRegion(key.Range{Start: -1, End: -1})
} }
@@ -553,8 +520,7 @@ func (w *window) SetInputHint(_ key.InputHint) {}
func (w *window) SetAnimating(anim bool) { func (w *window) SetAnimating(anim bool) {
w.anim = anim w.anim = anim
window := C.windowForView(w.view) if w.anim && w.visible {
if w.anim && window != 0 && C.isMiniaturized(window) == 0 {
w.displayLink.Start() w.displayLink.Start()
} else { } else {
w.displayLink.Stop() w.displayLink.Stop()
@@ -572,92 +538,23 @@ func (w *window) runOnMain(f func()) {
} }
//export gio_onKeys //export gio_onKeys
func gio_onKeys(h C.uintptr_t, event C.CFTypeRef, cstr C.CFTypeRef, ti C.double, mods C.NSUInteger, keyDown C.bool) { func gio_onKeys(h C.uintptr_t, cstr C.CFTypeRef, ti C.double, mods C.NSUInteger, keyDown C.bool) {
w := windowFor(h)
if w.keysDown == nil {
w.keysDown = make(map[key.Name]struct{})
}
str := nsstringToString(cstr) str := nsstringToString(cstr)
kmods := convertMods(mods) kmods := convertMods(mods)
ks := key.Release ks := key.Release
if keyDown { if keyDown {
ks = key.Press ks = key.Press
w.cmdKeys.eventStr = str
w.cmdKeys.eventMods = kmods
C.interpretKeyEvents(w.view, event)
} }
w := windowFor(h)
for _, k := range str { for _, k := range str {
if n, ok := convertKey(k); ok { if n, ok := convertKey(k); ok {
ke := key.Event{ w.ProcessEvent(key.Event{
Name: n, Name: n,
Modifiers: kmods, Modifiers: kmods,
State: ks, State: ks,
}
if keyDown {
w.keysDown[ke.Name] = struct{}{}
if _, isCmd := convertCommandKey(k); isCmd || kmods.Contain(key.ModCommand) {
// doCommandBySelector already processed the event.
return
}
} else {
if _, pressed := w.keysDown[n]; !pressed {
continue
}
delete(w.keysDown, n)
}
w.ProcessEvent(ke)
}
}
}
//export gio_onCommandBySelector
func gio_onCommandBySelector(h C.uintptr_t) C.bool {
w := windowFor(h)
ev := w.cmdKeys
w.cmdKeys = cmdKeys{}
handled := false
for _, k := range ev.eventStr {
n, ok := convertCommandKey(k)
if !ok && ev.eventMods.Contain(key.ModCommand) {
n, ok = convertKey(k)
}
if !ok {
continue
}
ke := key.Event{
Name: n,
Modifiers: ev.eventMods,
State: key.Press,
}
handled = w.processEvent(ke) || handled
}
return C.bool(handled)
}
//export gio_onFlagsChanged
func gio_onFlagsChanged(h C.uintptr_t, curMods C.NSUInteger) {
w := windowFor(h)
mods := []C.NSUInteger{C.NSControlKeyMask, C.NSAlternateKeyMask, C.NSShiftKeyMask, C.NSCommandKeyMask}
keys := []key.Name{key.NameCtrl, key.NameAlt, key.NameShift, key.NameCommand}
for i, mod := range mods {
wasPressed := w.lastMods&mod != 0
isPressed := curMods&mod != 0
if wasPressed != isPressed {
st := key.Release
if isPressed {
st = key.Press
}
w.ProcessEvent(key.Event{
Name: keys[i],
State: st,
}) })
} }
} }
w.lastMods = curMods
} }
//export gio_onText //export gio_onText
@@ -850,15 +747,6 @@ func gio_substringForProposedRange(h C.uintptr_t, crng C.NSRange, actual C.NSRan
//export gio_insertText //export gio_insertText
func gio_insertText(h C.uintptr_t, cstr C.CFTypeRef, crng C.NSRange) { func gio_insertText(h C.uintptr_t, cstr C.CFTypeRef, crng C.NSRange) {
w := windowFor(h) w := windowFor(h)
str := nsstringToString(cstr)
// macOS IME in some cases calls insertText for command keys such as backspace
// instead of doCommandBySelector.
for _, r := range str {
if _, ok := convertCommandKey(r); ok {
w.w.SetComposingRegion(key.Range{Start: -1, End: -1})
return
}
}
state := w.w.EditorState() state := w.w.EditorState()
rng := state.compose rng := state.compose
if rng.Start == -1 { if rng.Start == -1 {
@@ -870,6 +758,7 @@ func gio_insertText(h C.uintptr_t, cstr C.CFTypeRef, crng C.NSRange) {
End: state.RunesIndex(int(crng.location + crng.length)), End: state.RunesIndex(int(crng.location + crng.length)),
} }
} }
str := nsstringToString(cstr)
w.w.EditorReplace(rng, str) w.w.EditorReplace(rng, str)
w.w.SetComposingRegion(key.Range{Start: -1, End: -1}) w.w.SetComposingRegion(key.Range{Start: -1, End: -1})
start := rng.Start start := rng.Start
@@ -896,7 +785,7 @@ func gio_firstRectForCharacterRange(h C.uintptr_t, crng C.NSRange, actual C.NSRa
// Transform to NSView local coordinates (lower left origin, undo backing scale). // Transform to NSView local coordinates (lower left origin, undo backing scale).
scale := 1. / float32(C.getViewBackingScale(w.view)) scale := 1. / float32(C.getViewBackingScale(w.view))
height := float32(C.viewHeight(w.view)) height := float32(C.viewHeight(w.view))
local := f32.AffineId().Scale(f32.Pt(0, 0), f32.Pt(scale, -scale)).Offset(f32.Pt(0, height)) local := f32.Affine2D{}.Scale(f32.Pt(0, 0), f32.Pt(scale, -scale)).Offset(f32.Pt(0, height))
t := local.Mul(sel.Transform) t := local.Mul(sel.Transform)
bounds := f32.Rectangle{ bounds := f32.Rectangle{
Min: t.Transform(sel.Pos.Sub(f32.Pt(0, sel.Ascent))), Min: t.Transform(sel.Pos.Sub(f32.Pt(0, sel.Ascent))),
@@ -910,19 +799,24 @@ func gio_firstRectForCharacterRange(h C.uintptr_t, crng C.NSRange, actual C.NSRa
} }
func (w *window) draw() { func (w *window) draw() {
cnf := w.config
w.updateWindowMode()
if w.config != cnf {
w.ProcessEvent(ConfigEvent{Config: w.config})
}
select { select {
case <-w.redraw: case <-w.redraw:
default: default:
} }
w.visible = true
if w.anim { if w.anim {
w.SetAnimating(w.anim) w.SetAnimating(w.anim)
} }
sz := w.config.Size w.scale = float32(C.getViewBackingScale(w.view))
wf, hf := float32(C.viewWidth(w.view)), float32(C.viewHeight(w.view))
sz := image.Point{
X: int(wf*w.scale + .5),
Y: int(hf*w.scale + .5),
}
if sz != w.config.Size {
w.config.Size = sz
w.ProcessEvent(ConfigEvent{Config: w.config})
}
if sz.X == 0 || sz.Y == 0 { if sz.X == 0 || sz.Y == 0 {
return return
} }
@@ -930,7 +824,7 @@ func (w *window) draw() {
w.ProcessEvent(frameEvent{ w.ProcessEvent(frameEvent{
FrameEvent: FrameEvent{ FrameEvent: FrameEvent{
Now: time.Now(), Now: time.Now(),
Size: sz, Size: w.config.Size,
Metric: cfg, Metric: cfg,
}, },
Sync: true, Sync: true,
@@ -938,13 +832,8 @@ func (w *window) draw() {
} }
func (w *window) ProcessEvent(e event.Event) { func (w *window) ProcessEvent(e event.Event) {
w.processEvent(e) w.w.ProcessEvent(e)
}
func (w *window) processEvent(e event.Event) bool {
handled := w.w.ProcessEvent(e)
w.loop.FlushEvents() w.loop.FlushEvents()
return handled
} }
func (w *window) Event() event.Event { func (w *window) Event() event.Event {
@@ -978,6 +867,7 @@ func gio_onAttached(h C.uintptr_t, attached C.int) {
w.ProcessEvent(AppKitViewEvent{View: uintptr(w.view), Layer: uintptr(layer)}) w.ProcessEvent(AppKitViewEvent{View: uintptr(w.view), Layer: uintptr(layer)})
} else { } else {
w.ProcessEvent(AppKitViewEvent{}) w.ProcessEvent(AppKitViewEvent{})
w.visible = false
w.SetAnimating(w.anim) w.SetAnimating(w.anim)
} }
} }
@@ -992,6 +882,33 @@ func gio_onDestroy(h C.uintptr_t) {
w.view = 0 w.view = 0
} }
//export gio_onHide
func gio_onHide(h C.uintptr_t) {
w := windowFor(h)
w.visible = false
w.SetAnimating(w.anim)
}
//export gio_onShow
func gio_onShow(h C.uintptr_t) {
w := windowFor(h)
w.draw()
}
//export gio_onFullscreen
func gio_onFullscreen(h C.uintptr_t) {
w := windowFor(h)
w.config.Mode = Fullscreen
w.ProcessEvent(ConfigEvent{Config: w.config})
}
//export gio_onWindowed
func gio_onWindowed(h C.uintptr_t) {
w := windowFor(h)
w.config.Mode = Windowed
w.ProcessEvent(ConfigEvent{Config: w.config})
}
//export gio_onFinishLaunching //export gio_onFinishLaunching
func gio_onFinishLaunching() { func gio_onFinishLaunching() {
close(launched) close(launched)
@@ -1014,9 +931,10 @@ func newWindow(win *callbacks, options []Option) {
w.ProcessEvent(DestroyEvent{Err: err}) w.ProcessEvent(DestroyEvent{Err: err})
return return
} }
window := C.gio_createWindow(w.view, C.CGFloat(cnf.Size.X), C.CGFloat(cnf.Size.Y)) window := C.gio_createWindow(w.view, 0, 0, 0, 0, 0, 0)
// Release our reference now that the NSWindow has it. // Release our reference now that the NSWindow has it.
C.CFRelease(w.view) C.CFRelease(w.view)
w.updateWindowMode()
w.Configure(options) w.Configure(options)
if nextTopLeft.x == 0 && nextTopLeft.y == 0 { if nextTopLeft.x == 0 && nextTopLeft.y == 0 {
// cascadeTopLeftFromPoint treats (0, 0) as a no-op, // cascadeTopLeftFromPoint treats (0, 0) as a no-op,
@@ -1024,7 +942,6 @@ func newWindow(win *callbacks, options []Option) {
nextTopLeft = C.cascadeTopLeftFromPoint(window, nextTopLeft) nextTopLeft = C.cascadeTopLeftFromPoint(window, nextTopLeft)
} }
nextTopLeft = C.cascadeTopLeftFromPoint(window, nextTopLeft) nextTopLeft = C.cascadeTopLeftFromPoint(window, nextTopLeft)
C.makeFirstResponder(window, w.view)
// makeKeyAndOrderFront assumes ownership of our window reference. // makeKeyAndOrderFront assumes ownership of our window reference.
C.makeKeyAndOrderFront(window) C.makeKeyAndOrderFront(window)
}) })
@@ -1049,7 +966,9 @@ func (w *window) init(customRenderer bool) error {
return return
} }
w.runOnMain(func() { w.runOnMain(func() {
C.setNeedsDisplay(w.view) if w.visible {
C.setNeedsDisplay(w.view)
}
}) })
}) })
w.displayLink = dl w.displayLink = dl
@@ -1069,10 +988,10 @@ func osMain() {
C.gio_main() C.gio_main()
} }
func convertCommandKey(k rune) (key.Name, bool) { func convertKey(k rune) (key.Name, bool) {
var n key.Name var n key.Name
switch k { switch k {
case '\x1b': // ASCII escape. case 0x1b:
n = key.NameEscape n = key.NameEscape
case C.NSLeftArrowFunctionKey: case C.NSLeftArrowFunctionKey:
n = key.NameLeftArrow n = key.NameLeftArrow
@@ -1082,36 +1001,22 @@ func convertCommandKey(k rune) (key.Name, bool) {
n = key.NameUpArrow n = key.NameUpArrow
case C.NSDownArrowFunctionKey: case C.NSDownArrowFunctionKey:
n = key.NameDownArrow n = key.NameDownArrow
case '\r': case 0xd:
n = key.NameReturn n = key.NameReturn
case '\x03': case 0x3:
n = key.NameEnter n = key.NameEnter
case C.NSHomeFunctionKey: case C.NSHomeFunctionKey:
n = key.NameHome n = key.NameHome
case C.NSEndFunctionKey: case C.NSEndFunctionKey:
n = key.NameEnd n = key.NameEnd
case '\x7f', '\b': case 0x7f:
n = key.NameDeleteBackward n = key.NameDeleteBackward
case C.NSDeleteFunctionKey: case C.NSDeleteFunctionKey:
n = key.NameDeleteForward n = key.NameDeleteForward
case '\t', 0x19:
n = key.NameTab
case C.NSPageUpFunctionKey: case C.NSPageUpFunctionKey:
n = key.NamePageUp n = key.NamePageUp
case C.NSPageDownFunctionKey: case C.NSPageDownFunctionKey:
n = key.NamePageDown n = key.NamePageDown
default:
return "", false
}
return n, true
}
func convertKey(k rune) (key.Name, bool) {
if n, ok := convertCommandKey(k); ok {
return n, true
}
var n key.Name
switch k {
case C.NSF1FunctionKey: case C.NSF1FunctionKey:
n = key.NameF1 n = key.NameF1
case C.NSF2FunctionKey: case C.NSF2FunctionKey:
@@ -1136,6 +1041,8 @@ func convertKey(k rune) (key.Name, bool) {
n = key.NameF11 n = key.NameF11
case C.NSF12FunctionKey: case C.NSF12FunctionKey:
n = key.NameF12 n = key.NameF12
case 0x09, 0x19:
n = key.NameTab
case 0x20: case 0x20:
n = key.NameSpace n = key.NameSpace
default: default:
+22 -39
View File
@@ -23,22 +23,22 @@ __attribute__ ((visibility ("hidden"))) CALayer *gio_layerFactory(BOOL presentWi
- (void)windowWillMiniaturize:(NSNotification *)notification { - (void)windowWillMiniaturize:(NSNotification *)notification {
NSWindow *window = (NSWindow *)[notification object]; NSWindow *window = (NSWindow *)[notification object];
GioView *view = (GioView *)window.contentView; GioView *view = (GioView *)window.contentView;
gio_onDraw(view.handle); gio_onHide(view.handle);
} }
- (void)windowDidDeminiaturize:(NSNotification *)notification { - (void)windowDidDeminiaturize:(NSNotification *)notification {
NSWindow *window = (NSWindow *)[notification object]; NSWindow *window = (NSWindow *)[notification object];
GioView *view = (GioView *)window.contentView; GioView *view = (GioView *)window.contentView;
gio_onDraw(view.handle); gio_onShow(view.handle);
} }
- (void)windowWillEnterFullScreen:(NSNotification *)notification { - (void)windowWillEnterFullScreen:(NSNotification *)notification {
NSWindow *window = (NSWindow *)[notification object]; NSWindow *window = (NSWindow *)[notification object];
GioView *view = (GioView *)window.contentView; GioView *view = (GioView *)window.contentView;
gio_onDraw(view.handle); gio_onFullscreen(view.handle);
} }
- (void)windowWillExitFullScreen:(NSNotification *)notification { - (void)windowWillExitFullScreen:(NSNotification *)notification {
NSWindow *window = (NSWindow *)[notification object]; NSWindow *window = (NSWindow *)[notification object];
GioView *view = (GioView *)window.contentView; GioView *view = (GioView *)window.contentView;
gio_onDraw(view.handle); gio_onWindowed(view.handle);
} }
- (void)windowDidChangeScreen:(NSNotification *)notification { - (void)windowDidChangeScreen:(NSNotification *)notification {
NSWindow *window = (NSWindow *)[notification object]; NSWindow *window = (NSWindow *)[notification object];
@@ -132,25 +132,22 @@ static void handleMouse(GioView *view, NSEvent *event, int typ, CGFloat dx, CGFl
handleMouse(self, event, MOUSE_SCROLL, dx, dy); handleMouse(self, event, MOUSE_SCROLL, dx, dy);
} }
- (void)keyDown:(NSEvent *)event { - (void)keyDown:(NSEvent *)event {
NSString *keys = [event charactersIgnoringModifiers];
gio_onKeys(self.handle, (__bridge CFTypeRef)event, (__bridge CFTypeRef)keys, [event timestamp], [event modifierFlags], true);
}
- (void)flagsChanged:(NSEvent *)event {
[self interpretKeyEvents:[NSArray arrayWithObject:event]]; [self interpretKeyEvents:[NSArray arrayWithObject:event]];
gio_onFlagsChanged(self.handle, [event modifierFlags]); NSString *keys = [event charactersIgnoringModifiers];
gio_onKeys(self.handle, (__bridge CFTypeRef)keys, [event timestamp], [event modifierFlags], true);
} }
- (void)keyUp:(NSEvent *)event { - (void)keyUp:(NSEvent *)event {
NSString *keys = [event charactersIgnoringModifiers]; NSString *keys = [event charactersIgnoringModifiers];
gio_onKeys(self.handle, (__bridge CFTypeRef)event, (__bridge CFTypeRef)keys, [event timestamp], [event modifierFlags], false); gio_onKeys(self.handle, (__bridge CFTypeRef)keys, [event timestamp], [event modifierFlags], false);
} }
- (void)insertText:(id)string { - (void)insertText:(id)string {
gio_onText(self.handle, (__bridge CFTypeRef)string); gio_onText(self.handle, (__bridge CFTypeRef)string);
} }
- (void)doCommandBySelector:(SEL)action { - (void)doCommandBySelector:(SEL)sel {
if (!gio_onCommandBySelector(self.handle)) { // Don't pass commands up the responder chain.
[super doCommandBySelector:action]; // They will end up in a beep.
}
} }
- (BOOL)hasMarkedText { - (BOOL)hasMarkedText {
int res = gio_hasMarkedText(self.handle); int res = gio_hasMarkedText(self.handle);
return res ? YES : NO; return res ? YES : NO;
@@ -205,10 +202,10 @@ static void handleMouse(GioView *view, NSEvent *event, int typ, CGFloat dx, CGFl
return [[self window] convertRectToScreen:r]; return [[self window] convertRectToScreen:r];
} }
- (void)applicationWillUnhide:(NSNotification *)notification { - (void)applicationWillUnhide:(NSNotification *)notification {
gio_onDraw(self.handle); gio_onShow(self.handle);
} }
- (void)applicationDidHide:(NSNotification *)notification { - (void)applicationDidHide:(NSNotification *)notification {
gio_onDraw(self.handle); gio_onHide(self.handle);
} }
- (void)dealloc { - (void)dealloc {
gio_onDestroy(self.handle); gio_onDestroy(self.handle);
@@ -369,7 +366,7 @@ void gio_setCursor(NSUInteger curID) {
} }
} }
CFTypeRef gio_createWindow(CFTypeRef viewRef, CGFloat width, CGFloat height) { CFTypeRef gio_createWindow(CFTypeRef viewRef, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight) {
@autoreleasepool { @autoreleasepool {
NSRect rect = NSMakeRect(0, 0, width, height); NSRect rect = NSMakeRect(0, 0, width, height);
NSUInteger styleMask = NSTitledWindowMask | NSUInteger styleMask = NSTitledWindowMask |
@@ -381,9 +378,16 @@ CFTypeRef gio_createWindow(CFTypeRef viewRef, CGFloat width, CGFloat height) {
styleMask:styleMask styleMask:styleMask
backing:NSBackingStoreBuffered backing:NSBackingStoreBuffered
defer:NO]; defer:NO];
if (minWidth > 0 || minHeight > 0) {
window.contentMinSize = NSMakeSize(minWidth, minHeight);
}
if (maxWidth > 0 || maxHeight > 0) {
window.contentMaxSize = NSMakeSize(maxWidth, maxHeight);
}
[window setAcceptsMouseMovedEvents:YES]; [window setAcceptsMouseMovedEvents:YES];
NSView *view = (__bridge NSView *)viewRef; NSView *view = (__bridge NSView *)viewRef;
[window setContentView:view]; [window setContentView:view];
[window makeFirstResponder:view];
window.delegate = globalWindowDel; window.delegate = globalWindowDel;
return (__bridge_retained CFTypeRef)window; return (__bridge_retained CFTypeRef)window;
} }
@@ -420,6 +424,7 @@ void gio_viewSetHandle(CFTypeRef viewRef, uintptr_t handle) {
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
[NSApp activateIgnoringOtherApps:YES]; [NSApp activateIgnoringOtherApps:YES];
gio_onFinishLaunching();
} }
@end @end
@@ -450,25 +455,3 @@ void gio_main() {
[NSApp run]; [NSApp run];
} }
} }
@interface AppListener : NSObject
@end
static AppListener *appListener;
@implementation AppListener
- (void)launchFinished:(NSNotification *)notification {
appListener = nil;
gio_onFinishLaunching();
}
@end
void gio_init() {
@autoreleasepool {
appListener = [[AppListener alloc] init];
[[NSNotificationCenter defaultCenter] addObserver:appListener
selector:@selector(launchFinished:)
name:NSApplicationDidFinishLaunchingNotification
object:nil];
}
}
+14 -26
View File
@@ -116,8 +116,6 @@ type wlSeat struct {
// The most recent input serial. // The most recent input serial.
serial C.uint32_t serial C.uint32_t
// The most recent pointer enter serial.
pointerSerial C.uint32_t
pointerFocus *window pointerFocus *window
keyboardFocus *window keyboardFocus *window
@@ -156,6 +154,7 @@ type repeatState struct {
type window struct { type window struct {
w *callbacks w *callbacks
disp *wlDisplay disp *wlDisplay
seat *wlSeat
surf *C.struct_wl_surface surf *C.struct_wl_surface
wmSurf *C.struct_xdg_surface wmSurf *C.struct_xdg_surface
topLvl *C.struct_xdg_toplevel topLvl *C.struct_xdg_toplevel
@@ -852,8 +851,8 @@ func gio_onTouchCancel(data unsafe.Pointer, touch *C.struct_wl_touch) {
func gio_onPointerEnter(data unsafe.Pointer, pointer *C.struct_wl_pointer, serial C.uint32_t, surf *C.struct_wl_surface, x, y C.wl_fixed_t) { func gio_onPointerEnter(data unsafe.Pointer, pointer *C.struct_wl_pointer, serial C.uint32_t, surf *C.struct_wl_surface, x, y C.wl_fixed_t) {
s := callbackLoad(data).(*wlSeat) s := callbackLoad(data).(*wlSeat)
s.serial = serial s.serial = serial
s.pointerSerial = serial
w := callbackLoad(unsafe.Pointer(surf)).(*window) w := callbackLoad(unsafe.Pointer(surf)).(*window)
w.seat = s
s.pointerFocus = w s.pointerFocus = w
w.setCursor(pointer, serial) w.setCursor(pointer, serial)
w.lastPos = f32.Point{X: fromFixed(x), Y: fromFixed(y)} w.lastPos = f32.Point{X: fromFixed(x), Y: fromFixed(y)}
@@ -862,9 +861,9 @@ func gio_onPointerEnter(data unsafe.Pointer, pointer *C.struct_wl_pointer, seria
//export gio_onPointerLeave //export gio_onPointerLeave
func gio_onPointerLeave(data unsafe.Pointer, p *C.struct_wl_pointer, serial C.uint32_t, surf *C.struct_wl_surface) { func gio_onPointerLeave(data unsafe.Pointer, p *C.struct_wl_pointer, serial C.uint32_t, surf *C.struct_wl_surface) {
w := callbackLoad(unsafe.Pointer(surf)).(*window) w := callbackLoad(unsafe.Pointer(surf)).(*window)
w.seat = nil
s := callbackLoad(data).(*wlSeat) s := callbackLoad(data).(*wlSeat)
s.serial = serial s.serial = serial
s.pointerFocus = nil
if w.inCompositor { if w.inCompositor {
w.inCompositor = false w.inCompositor = false
w.ProcessEvent(pointer.Event{Kind: pointer.Cancel}) w.ProcessEvent(pointer.Event{Kind: pointer.Cancel})
@@ -884,13 +883,11 @@ func gio_onPointerButton(data unsafe.Pointer, p *C.struct_wl_pointer, serial, t,
s := callbackLoad(data).(*wlSeat) s := callbackLoad(data).(*wlSeat)
s.serial = serial s.serial = serial
w := s.pointerFocus w := s.pointerFocus
// From Linux: include/uapi/linux/input-event-codes.h // From linux-event-codes.h.
const ( const (
BTN_LEFT = 0x110 BTN_LEFT = 0x110
BTN_RIGHT = 0x111 BTN_RIGHT = 0x111
BTN_MIDDLE = 0x112 BTN_MIDDLE = 0x112
BTN_SIDE = 0x113
BTN_EXTRA = 0x114
) )
var btn pointer.Buttons var btn pointer.Buttons
switch wbtn { switch wbtn {
@@ -900,10 +897,6 @@ func gio_onPointerButton(data unsafe.Pointer, p *C.struct_wl_pointer, serial, t,
btn = pointer.ButtonSecondary btn = pointer.ButtonSecondary
case BTN_MIDDLE: case BTN_MIDDLE:
btn = pointer.ButtonTertiary btn = pointer.ButtonTertiary
case BTN_SIDE:
btn = pointer.ButtonQuaternary
case BTN_EXTRA:
btn = pointer.ButtonQuinary
default: default:
return return
} }
@@ -970,9 +963,6 @@ func gio_onPointerAxis(data unsafe.Pointer, p *C.struct_wl_pointer, t, axis C.ui
func gio_onPointerFrame(data unsafe.Pointer, p *C.struct_wl_pointer) { func gio_onPointerFrame(data unsafe.Pointer, p *C.struct_wl_pointer) {
s := callbackLoad(data).(*wlSeat) s := callbackLoad(data).(*wlSeat)
w := s.pointerFocus w := s.pointerFocus
if w == nil {
return
}
w.flushScroll() w.flushScroll()
w.flushFling() w.flushFling()
} }
@@ -1153,17 +1143,16 @@ func (w *window) Perform(actions system.Action) {
} }
func (w *window) move(serial C.uint32_t) { func (w *window) move(serial C.uint32_t) {
s := w.disp.seat s := w.seat
if w.inCompositor || s.pointerFocus != w { if !w.inCompositor && s != nil {
return w.inCompositor = true
C.xdg_toplevel_move(w.topLvl, s.seat, serial)
} }
w.inCompositor = true
C.xdg_toplevel_move(w.topLvl, s.seat, serial)
} }
func (w *window) resize(serial, edge C.uint32_t) { func (w *window) resize(serial, edge C.uint32_t) {
s := w.disp.seat s := w.seat
if w.inCompositor || s.pointerFocus != w { if w.inCompositor || s == nil {
return return
} }
w.inCompositor = true w.inCompositor = true
@@ -1176,12 +1165,11 @@ func (w *window) SetCursor(cursor pointer.Cursor) {
} }
func (w *window) updateCursor() { func (w *window) updateCursor() {
s := w.disp.seat ptr := w.disp.seat.pointer
ptr := s.pointer if ptr == nil {
if ptr == nil || s.pointerFocus != w {
return return
} }
w.setCursor(ptr, s.pointerSerial) w.setCursor(ptr, w.serial)
} }
func (w *window) setCursor(pointer *C.struct_wl_pointer, serial C.uint32_t) { func (w *window) setCursor(pointer *C.struct_wl_pointer, serial C.uint32_t) {
@@ -1190,7 +1178,7 @@ func (w *window) setCursor(pointer *C.struct_wl_pointer, serial C.uint32_t) {
c = w.cursor.cursor c = w.cursor.cursor
} }
if c == nil { if c == nil {
C.wl_pointer_set_cursor(pointer, serial, nil, 0, 0) C.wl_pointer_set_cursor(pointer, w.serial, nil, 0, 0)
return return
} }
// Get images[0]. // Get images[0].
+102 -164
View File
@@ -36,22 +36,24 @@ type Win32ViewEvent struct {
} }
type window struct { type window struct {
hwnd syscall.Handle hwnd syscall.Handle
hdc syscall.Handle hdc syscall.Handle
w *callbacks w *callbacks
pointerBtns pointer.Buttons
// cursorIn tracks whether the cursor was inside the window according // cursorIn tracks whether the cursor was inside the window according
// to the most recent WM_SETCURSOR. // to the most recent WM_SETCURSOR.
cursorIn bool cursorIn bool
cursor syscall.Handle cursor syscall.Handle
// placement saves the previous window position when in full screen mode.
placement *windows.WindowPlacement
animating bool animating bool
borderSize image.Point borderSize image.Point
config Config config Config
// frameDims stores the last seen window frame width and height. loop *eventLoop
frameDims image.Point
loop *eventLoop
} }
const _WM_WAKEUP = windows.WM_USER + iota const _WM_WAKEUP = windows.WM_USER + iota
@@ -106,8 +108,8 @@ func newWindow(win *callbacks, options []Option) {
} }
winMap.Store(w.hwnd, w) winMap.Store(w.hwnd, w)
defer winMap.Delete(w.hwnd) defer winMap.Delete(w.hwnd)
w.Configure(options)
w.ProcessEvent(Win32ViewEvent{HWND: uintptr(w.hwnd)}) w.ProcessEvent(Win32ViewEvent{HWND: uintptr(w.hwnd)})
w.Configure(options)
windows.SetForegroundWindow(w.hwnd) windows.SetForegroundWindow(w.hwnd)
windows.SetFocus(w.hwnd) windows.SetFocus(w.hwnd)
// Since the window class for the cursor is null, // Since the window class for the cursor is null,
@@ -174,12 +176,6 @@ func (w *window) init() error {
if err != nil { if err != nil {
return err return err
} }
if err := windows.RegisterTouchWindow(hwnd, 0); err != nil {
return err
}
if err := windows.EnableMouseInPointer(1); err != nil {
return err
}
w.hdc, err = windows.GetDC(hwnd) w.hdc, err = windows.GetDC(hwnd)
if err != nil { if err != nil {
windows.DestroyWindow(hwnd) windows.DestroyWindow(hwnd)
@@ -189,39 +185,21 @@ func (w *window) init() error {
return nil return nil
} }
// update handles changes done by the user, and updates the configuration. // update() handles changes done by the user, and updates the configuration.
// It reads the window style and size/position and updates w.config. // It reads the window style and size/position and updates w.config.
// If anything has changed it emits a ConfigEvent to notify the application. // If anything has changed it emits a ConfigEvent to notify the application.
func (w *window) update() { func (w *window) update() {
p := windows.GetWindowPlacement(w.hwnd) cr := windows.GetClientRect(w.hwnd)
if !p.IsMinimized() { w.config.Size = image.Point{
r := windows.GetWindowRect(w.hwnd) X: int(cr.Right - cr.Left),
cr := windows.GetClientRect(w.hwnd) Y: int(cr.Bottom - cr.Top),
w.config.Size = image.Point{
X: int(cr.Right - cr.Left),
Y: int(cr.Bottom - cr.Top),
}
w.frameDims = image.Point{
X: int(r.Right - r.Left),
Y: int(r.Bottom - r.Top),
}.Sub(w.config.Size)
} }
w.borderSize = image.Pt( w.borderSize = image.Pt(
windows.GetSystemMetrics(windows.SM_CXSIZEFRAME), windows.GetSystemMetrics(windows.SM_CXSIZEFRAME),
windows.GetSystemMetrics(windows.SM_CYSIZEFRAME), windows.GetSystemMetrics(windows.SM_CYSIZEFRAME),
) )
style := windows.GetWindowLong(w.hwnd, windows.GWL_STYLE)
switch {
case p.IsMaximized() && style&windows.WS_OVERLAPPEDWINDOW != 0:
w.config.Mode = Maximized
case p.IsMaximized():
w.config.Mode = Fullscreen
default:
w.config.Mode = Windowed
}
w.ProcessEvent(ConfigEvent{Config: w.config}) w.ProcessEvent(ConfigEvent{Config: w.config})
w.draw(true)
} }
func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr { func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr {
@@ -270,32 +248,18 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
return 0 return 0
} }
} }
case windows.WM_POINTERDOWN, windows.WM_POINTERUP, windows.WM_POINTERUPDATE, windows.WM_POINTERCAPTURECHANGED: case windows.WM_LBUTTONDOWN:
pid := getPointerIDwParam(wParam) w.pointerButton(pointer.ButtonPrimary, true, lParam, getModifiers())
pi, err := windows.GetPointerInfo(uint32(pid)) case windows.WM_LBUTTONUP:
if err != nil { w.pointerButton(pointer.ButtonPrimary, false, lParam, getModifiers())
panic(err) case windows.WM_RBUTTONDOWN:
} w.pointerButton(pointer.ButtonSecondary, true, lParam, getModifiers())
switch msg { case windows.WM_RBUTTONUP:
case windows.WM_POINTERDOWN: w.pointerButton(pointer.ButtonSecondary, false, lParam, getModifiers())
windows.SetCapture(w.hwnd) case windows.WM_MBUTTONDOWN:
case windows.WM_POINTERUP: w.pointerButton(pointer.ButtonTertiary, true, lParam, getModifiers())
windows.ReleaseCapture() case windows.WM_MBUTTONUP:
} w.pointerButton(pointer.ButtonTertiary, false, lParam, getModifiers())
kind := pointer.Move
switch pi.ButtonChangeType {
case windows.POINTER_CHANGE_FIRSTBUTTON_DOWN, windows.POINTER_CHANGE_SECONDBUTTON_DOWN, windows.POINTER_CHANGE_THIRDBUTTON_DOWN, windows.POINTER_CHANGE_FOURTHBUTTON_DOWN, windows.POINTER_CHANGE_FIFTHBUTTON_DOWN:
kind = pointer.Press
case windows.POINTER_CHANGE_FIRSTBUTTON_UP, windows.POINTER_CHANGE_SECONDBUTTON_UP, windows.POINTER_CHANGE_THIRDBUTTON_UP, windows.POINTER_CHANGE_FOURTHBUTTON_UP, windows.POINTER_CHANGE_FIFTHBUTTON_UP:
kind = pointer.Release
}
if (pi.PointerFlags&windows.POINTER_FLAG_CANCELED != 0) || (msg == windows.WM_POINTERCAPTURECHANGED) {
kind = pointer.Cancel
}
w.pointerUpdate(pi, pid, kind, lParam)
case windows.WM_CANCELMODE: case windows.WM_CANCELMODE:
w.ProcessEvent(pointer.Event{ w.ProcessEvent(pointer.Event{
Kind: pointer.Cancel, Kind: pointer.Cancel,
@@ -315,14 +279,24 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
np := windows.Point{X: int32(x), Y: int32(y)} np := windows.Point{X: int32(x), Y: int32(y)}
windows.ScreenToClient(w.hwnd, &np) windows.ScreenToClient(w.hwnd, &np)
return w.hitTest(int(np.X), int(np.Y)) return w.hitTest(int(np.X), int(np.Y))
case windows.WM_POINTERWHEEL: case windows.WM_MOUSEMOVE:
x, y := coordsFromlParam(lParam)
p := f32.Point{X: float32(x), Y: float32(y)}
w.ProcessEvent(pointer.Event{
Kind: pointer.Move,
Source: pointer.Mouse,
Position: p,
Buttons: w.pointerBtns,
Time: windows.GetMessageTime(),
Modifiers: getModifiers(),
})
case windows.WM_MOUSEWHEEL:
w.scrollEvent(wParam, lParam, false, getModifiers()) w.scrollEvent(wParam, lParam, false, getModifiers())
case windows.WM_POINTERHWHEEL: case windows.WM_MOUSEHWHEEL:
w.scrollEvent(wParam, lParam, true, getModifiers()) w.scrollEvent(wParam, lParam, true, getModifiers())
case windows.WM_DESTROY: case windows.WM_DESTROY:
w.ProcessEvent(Win32ViewEvent{}) w.ProcessEvent(Win32ViewEvent{})
w.ProcessEvent(DestroyEvent{}) w.ProcessEvent(DestroyEvent{})
w.w = nil
if w.hdc != 0 { if w.hdc != 0 {
windows.ReleaseDC(w.hdc) windows.ReleaseDC(w.hdc)
w.hdc = 0 w.hdc = 0
@@ -330,7 +304,6 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
// The system destroys the HWND for us. // The system destroys the HWND for us.
w.hwnd = 0 w.hwnd = 0
windows.PostQuitMessage(0) windows.PostQuitMessage(0)
return 0
case windows.WM_NCCALCSIZE: case windows.WM_NCCALCSIZE:
if w.config.Decorated { if w.config.Decorated {
// Let Windows handle decorations. // Let Windows handle decorations.
@@ -353,33 +326,45 @@ 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_STYLECHANGED:
w.update()
case windows.WM_WINDOWPOSCHANGED:
w.update()
case windows.WM_SIZE: case windows.WM_SIZE:
w.update() w.update()
switch wParam {
case windows.SIZE_MINIMIZED:
w.config.Mode = Minimized
case windows.SIZE_MAXIMIZED:
w.config.Mode = Maximized
case windows.SIZE_RESTORED:
if w.config.Mode != Fullscreen {
w.config.Mode = Windowed
}
}
case windows.WM_GETMINMAXINFO: case windows.WM_GETMINMAXINFO:
mm := (*windows.MinMaxInfo)(unsafe.Pointer(lParam)) mm := (*windows.MinMaxInfo)(unsafe.Pointer(lParam))
var bw, bh int32
var frameDims image.Point
if w.config.Decorated { if w.config.Decorated {
frameDims = w.frameDims r := windows.GetWindowRect(w.hwnd)
cr := windows.GetClientRect(w.hwnd)
bw = r.Right - r.Left - (cr.Right - cr.Left)
bh = r.Bottom - r.Top - (cr.Bottom - cr.Top)
} }
if p := w.config.MinSize; p.X > 0 || p.Y > 0 { if p := w.config.MinSize; p.X > 0 || p.Y > 0 {
p = p.Add(frameDims)
mm.PtMinTrackSize = windows.Point{ mm.PtMinTrackSize = windows.Point{
X: int32(p.X), X: int32(p.X) + bw,
Y: int32(p.Y), Y: int32(p.Y) + bh,
} }
} }
if p := w.config.MaxSize; p.X > 0 || p.Y > 0 { if p := w.config.MaxSize; p.X > 0 || p.Y > 0 {
p = p.Add(frameDims)
mm.PtMaxTrackSize = windows.Point{ mm.PtMaxTrackSize = windows.Point{
X: int32(p.X), X: int32(p.X) + bw,
Y: int32(p.Y), Y: int32(p.Y) + bh,
} }
} }
return 0 return 0
@@ -471,6 +456,9 @@ func getModifiers() key.Modifiers {
// hitTest returns the non-client area hit by the point, needed to // hitTest returns the non-client area hit by the point, needed to
// process WM_NCHITTEST. // process WM_NCHITTEST.
func (w *window) hitTest(x, y int) uintptr { func (w *window) hitTest(x, y int) uintptr {
if w.config.Mode == Fullscreen {
return windows.HTCLIENT
}
if w.config.Mode != Windowed { if w.config.Mode != Windowed {
// Only windowed mode should allow resizing. // Only windowed mode should allow resizing.
return windows.HTCLIENT return windows.HTCLIENT
@@ -507,28 +495,34 @@ func (w *window) hitTest(x, y int) uintptr {
return windows.HTCLIENT return windows.HTCLIENT
} }
func (w *window) pointerUpdate(pi windows.PointerInfo, pid pointer.ID, kind pointer.Kind, lParam uintptr) { func (w *window) pointerButton(btn pointer.Buttons, press bool, lParam uintptr, kmods key.Modifiers) {
if !w.config.Focused { if !w.config.Focused {
windows.SetFocus(w.hwnd) windows.SetFocus(w.hwnd)
} }
src := pointer.Touch var kind pointer.Kind
if pi.PointerType == windows.PT_MOUSE { if press {
src = pointer.Mouse kind = pointer.Press
if w.pointerBtns == 0 {
windows.SetCapture(w.hwnd)
}
w.pointerBtns |= btn
} else {
kind = pointer.Release
w.pointerBtns &^= btn
if w.pointerBtns == 0 {
windows.ReleaseCapture()
}
} }
x, y := coordsFromlParam(lParam) x, y := coordsFromlParam(lParam)
np := windows.Point{X: int32(x), Y: int32(y)} p := f32.Point{X: float32(x), Y: float32(y)}
windows.ScreenToClient(w.hwnd, &np)
p := f32.Point{X: float32(np.X), Y: float32(np.Y)}
w.ProcessEvent(pointer.Event{ w.ProcessEvent(pointer.Event{
Kind: kind, Kind: kind,
Source: src, Source: pointer.Mouse,
Position: p, Position: p,
PointerID: pid, Buttons: w.pointerBtns,
Buttons: getPointerButtons(pi),
Time: windows.GetMessageTime(), Time: windows.GetMessageTime(),
Modifiers: getModifiers(), Modifiers: kmods,
}) })
} }
@@ -539,12 +533,6 @@ func coordsFromlParam(lParam uintptr) (int, int) {
} }
func (w *window) scrollEvent(wParam, lParam uintptr, horizontal bool, kmods key.Modifiers) { func (w *window) scrollEvent(wParam, lParam uintptr, horizontal bool, kmods key.Modifiers) {
pid := getPointerIDwParam(wParam)
pi, err := windows.GetPointerInfo(uint32(pid))
if err != nil {
panic(err)
}
x, y := coordsFromlParam(lParam) x, y := coordsFromlParam(lParam)
// The WM_MOUSEWHEEL coordinates are in screen coordinates, in contrast // The WM_MOUSEWHEEL coordinates are in screen coordinates, in contrast
// to other mouse events. // to other mouse events.
@@ -567,7 +555,7 @@ func (w *window) scrollEvent(wParam, lParam uintptr, horizontal bool, kmods key.
Kind: pointer.Scroll, Kind: pointer.Scroll,
Source: pointer.Mouse, Source: pointer.Mouse,
Position: p, Position: p,
Buttons: getPointerButtons(pi), Buttons: w.pointerBtns,
Scroll: sp, Scroll: sp,
Modifiers: kmods, Modifiers: kmods,
Time: windows.GetMessageTime(), Time: windows.GetMessageTime(),
@@ -580,8 +568,7 @@ func (w *window) runLoop() {
loop: loop:
for { for {
anim := w.animating anim := w.animating
p := windows.GetWindowPlacement(w.hwnd) if anim && !windows.PeekMessage(msg, 0, 0, 0, windows.PM_NOREMOVE) {
if anim && !p.IsMinimized() && !windows.PeekMessage(msg, 0, 0, 0, windows.PM_NOREMOVE) {
w.draw(false) w.draw(false)
continue continue
} }
@@ -704,13 +691,8 @@ func (w *window) readClipboard() error {
func (w *window) Configure(options []Option) { func (w *window) Configure(options []Option) {
dpi := windows.GetSystemDPI() dpi := windows.GetSystemDPI()
metric := configForDPI(dpi) metric := configForDPI(dpi)
cnf := w.config w.config.apply(metric, options)
cnf.apply(metric, options) windows.SetWindowText(w.hwnd, w.config.Title)
w.config.Title = cnf.Title
w.config.Decorated = cnf.Decorated
w.config.MinSize = cnf.MinSize
w.config.MaxSize = cnf.MaxSize
windows.SetWindowText(w.hwnd, cnf.Title)
style := windows.GetWindowLong(w.hwnd, windows.GWL_STYLE) style := windows.GetWindowLong(w.hwnd, windows.GWL_STYLE)
var showMode int32 var showMode int32
@@ -718,7 +700,7 @@ func (w *window) Configure(options []Option) {
swpStyle := uintptr(windows.SWP_NOZORDER | windows.SWP_FRAMECHANGED) swpStyle := uintptr(windows.SWP_NOZORDER | windows.SWP_FRAMECHANGED)
winStyle := uintptr(windows.WS_OVERLAPPEDWINDOW) winStyle := uintptr(windows.WS_OVERLAPPEDWINDOW)
style &^= winStyle style &^= winStyle
switch cnf.Mode { switch w.config.Mode {
case Minimized: case Minimized:
style |= winStyle style |= winStyle
swpStyle |= windows.SWP_NOMOVE | windows.SWP_NOSIZE swpStyle |= windows.SWP_NOMOVE | windows.SWP_NOSIZE
@@ -733,13 +715,13 @@ func (w *window) Configure(options []Option) {
style |= winStyle style |= winStyle
showMode = windows.SW_SHOWNORMAL showMode = windows.SW_SHOWNORMAL
// Get target for client area size. // Get target for client area size.
width = int32(cnf.Size.X) width = int32(w.config.Size.X)
height = int32(cnf.Size.Y) height = int32(w.config.Size.Y)
// Get the current window size and position. // Get the current window size and position.
wr := windows.GetWindowRect(w.hwnd) wr := windows.GetWindowRect(w.hwnd)
x = wr.Left x = wr.Left
y = wr.Top y = wr.Top
if cnf.Decorated { if w.config.Decorated {
// Compute client size and position. Note that the client size is // Compute client size and position. Note that the client size is
// equal to the window size when we are in control of decorations. // equal to the window size when we are in control of decorations.
r := windows.Rect{ r := windows.Rect{
@@ -749,27 +731,25 @@ func (w *window) Configure(options []Option) {
windows.AdjustWindowRectEx(&r, uint32(style), 0, dwExStyle) windows.AdjustWindowRectEx(&r, uint32(style), 0, dwExStyle)
width = r.Right - r.Left width = r.Right - r.Left
height = r.Bottom - r.Top height = r.Bottom - r.Top
} else { }
if !w.config.Decorated {
// Enable drop shadows when we draw decorations. // Enable drop shadows when we draw decorations.
windows.DwmExtendFrameIntoClientArea(w.hwnd, windows.Margins{-1, -1, -1, -1}) windows.DwmExtendFrameIntoClientArea(w.hwnd, windows.Margins{-1, -1, -1, -1})
} }
case Fullscreen: case Fullscreen:
swpStyle |= windows.SWP_NOMOVE | windows.SWP_NOSIZE swpStyle |= windows.SWP_NOMOVE | windows.SWP_NOSIZE
mi := windows.GetMonitorInfo(w.hwnd)
x, y = mi.Monitor.Left, mi.Monitor.Top
width = mi.Monitor.Right - mi.Monitor.Left
height = mi.Monitor.Bottom - mi.Monitor.Top
showMode = windows.SW_SHOWMAXIMIZED showMode = windows.SW_SHOWMAXIMIZED
} }
// Disable window resizing if MinSize and MaxSize are equal.
if cnf.MaxSize != (image.Point{}) && cnf.MinSize == cnf.MaxSize {
style &^= windows.WS_MAXIMIZEBOX
style &^= windows.WS_THICKFRAME
}
// Note: these invocation all trigger the windows callback method which may process a pending system.ActionCenter
// action, so SetWindowPos should come first so as to not "overwrite" system.ActionCenter.
windows.SetWindowPos(w.hwnd, 0, x, y, width, height, swpStyle)
windows.SetWindowLong(w.hwnd, windows.GWL_STYLE, style) windows.SetWindowLong(w.hwnd, windows.GWL_STYLE, style)
windows.SetWindowPos(w.hwnd, 0, x, y, width, height, swpStyle)
windows.ShowWindow(w.hwnd, showMode) windows.ShowWindow(w.hwnd, showMode)
w.update()
} }
func (w *window) WriteClipboard(mime string, s []byte) { func (w *window) WriteClipboard(mime string, s []byte) {
@@ -1010,45 +990,3 @@ func (Win32ViewEvent) ImplementsEvent() {}
func (w Win32ViewEvent) Valid() bool { func (w Win32ViewEvent) Valid() bool {
return w != (Win32ViewEvent{}) return w != (Win32ViewEvent{})
} }
// LOWORD (minwindef.h)
func loWord(val uint32) uint16 {
return uint16(val & 0xFFFF)
}
// GET_POINTERID_WPARAM (winuser.h)
func getPointerIDwParam(wParam uintptr) pointer.ID {
return pointer.ID(loWord(uint32(wParam)))
}
func getPointerButtons(pi windows.PointerInfo) pointer.Buttons {
var btns pointer.Buttons
if pi.PointerFlags&windows.POINTER_FLAG_FIRSTBUTTON != 0 {
btns |= pointer.ButtonPrimary
} else {
btns &^= pointer.ButtonPrimary
}
if pi.PointerFlags&windows.POINTER_FLAG_SECONDBUTTON != 0 {
btns |= pointer.ButtonSecondary
} else {
btns &^= pointer.ButtonSecondary
}
if pi.PointerFlags&windows.POINTER_FLAG_THIRDBUTTON != 0 {
btns |= pointer.ButtonTertiary
} else {
btns &^= pointer.ButtonTertiary
}
if pi.PointerFlags&windows.POINTER_FLAG_FOURTHBUTTON != 0 {
btns |= pointer.ButtonQuaternary
} else {
btns &^= pointer.ButtonQuaternary
}
if pi.PointerFlags&windows.POINTER_FLAG_FIFTHBUTTON != 0 {
btns |= pointer.ButtonQuinary
} else {
btns &^= pointer.ButtonQuinary
}
return btns
}
+3 -2
View File
@@ -26,7 +26,6 @@ package app
*/ */
import "C" import "C"
import ( import (
"errors" "errors"
"fmt" "fmt"
@@ -752,7 +751,9 @@ func (h *x11EventHandler) handleEvents() bool {
return redraw return redraw
} }
var x11Threads sync.Once var (
x11Threads sync.Once
)
func init() { func init() {
x11Driver = newX11Window x11Driver = newX11Window
+1 -1
View File
@@ -175,7 +175,7 @@ func (c *vkContext) refresh(surf vk.Surface, width, height int) error {
if err != nil { if err != nil {
return err return err
} }
minExt, maxExt := vk.SurfaceCapabilitiesMinExtent(caps), vk.SurfaceCapabilitiesMaxExtent(caps) minExt, maxExt := caps.MinExtent(), caps.MaxExtent()
if width < minExt.X || maxExt.X < width || height < minExt.Y || maxExt.Y < height { if width < minExt.X || maxExt.X < width || height < minExt.Y || maxExt.Y < height {
return errOutOfDate return errOutOfDate
} }
+17 -19
View File
@@ -89,9 +89,6 @@ type Window struct {
} }
imeState editorState imeState editorState
driver driver driver driver
// gpuErr tracks the GPU error that is to be reported when
// the window is closed.
gpuErr error
// invMu protects mayInvalidate. // invMu protects mayInvalidate.
invMu sync.Mutex invMu sync.Mutex
@@ -142,11 +139,8 @@ func (w *Window) validateAndProcess(size image.Point, sync bool, frame *op.Ops,
if w.gpu == nil && !w.nocontext { if w.gpu == nil && !w.nocontext {
var err error var err error
if w.ctx == nil { if w.ctx == nil {
if w.ctx, err = w.driver.NewContext(); err != nil { w.ctx, err = w.driver.NewContext()
return err if err != nil {
}
if err = w.ctx.Lock(); err != nil {
w.destroyGPU()
return err return err
} }
sync = true sync = true
@@ -166,6 +160,12 @@ func (w *Window) validateAndProcess(size image.Point, sync bool, frame *op.Ops,
return err return err
} }
} }
if w.ctx != nil {
if err := w.ctx.Lock(); err != nil {
w.destroyGPU()
return err
}
}
if w.gpu == nil && !w.nocontext { if w.gpu == nil && !w.nocontext {
gpu, err := gpu.New(w.ctx.API()) gpu, err := gpu.New(w.ctx.API())
if err != nil { if err != nil {
@@ -197,6 +197,7 @@ func (w *Window) validateAndProcess(size image.Point, sync bool, frame *op.Ops,
var err error var err error
if w.gpu != nil { if w.gpu != nil {
err = w.ctx.Present() err = w.ctx.Present()
w.ctx.Unlock()
} }
return err return err
} }
@@ -226,8 +227,7 @@ func (w *Window) processFrame(frame *op.Ops, ack chan<- struct{}) {
w.lastFrame.deco.Add(wrapper) w.lastFrame.deco.Add(wrapper)
if err := w.validateAndProcess(w.lastFrame.size, w.lastFrame.sync, wrapper, ack); err != nil { if err := w.validateAndProcess(w.lastFrame.size, w.lastFrame.sync, wrapper, ack); err != nil {
w.destroyGPU() w.destroyGPU()
w.gpuErr = err w.driver.ProcessEvent(DestroyEvent{Err: err})
w.driver.Perform(system.ActionClose)
return return
} }
w.updateState() w.updateState()
@@ -390,8 +390,6 @@ func (c *callbacks) SetDriver(d driver) {
if d == nil { if d == nil {
panic("nil driver") panic("nil driver")
} }
c.w.invMu.Lock()
defer c.w.invMu.Unlock()
c.w.driver = d c.w.driver = d
} }
@@ -440,7 +438,10 @@ func (c *callbacks) SetComposingRegion(r key.Range) {
func (c *callbacks) EditorInsert(text string) { func (c *callbacks) EditorInsert(text string) {
sel := c.w.imeState.Selection.Range sel := c.w.imeState.Selection.Range
c.EditorReplace(sel, text) c.EditorReplace(sel, text)
start := min(sel.End, sel.Start) start := sel.Start
if sel.End < start {
start = sel.End
}
sel.Start = start + utf8.RuneCountInString(text) sel.Start = start + utf8.RuneCountInString(text)
sel.End = sel.Start sel.End = sel.Start
c.SetEditorSelection(sel) c.SetEditorSelection(sel)
@@ -636,14 +637,11 @@ func (w *Window) processEvent(e event.Event) bool {
e2.Size = e2.Size.Sub(offset) e2.Size = e2.Size.Sub(offset)
w.coalesced.frame = &e2 w.coalesced.frame = &e2
case DestroyEvent: case DestroyEvent:
if w.gpuErr != nil {
e2.Err = w.gpuErr
}
w.destroyGPU() w.destroyGPU()
w.invMu.Lock() w.invMu.Lock()
w.mayInvalidate = false w.mayInvalidate = false
w.driver = nil
w.invMu.Unlock() w.invMu.Unlock()
w.driver = nil
if q := w.timer.quit; q != nil { if q := w.timer.quit; q != nil {
q <- struct{}{} q <- struct{}{}
<-q <-q
@@ -658,7 +656,6 @@ func (w *Window) processEvent(e event.Event) bool {
} }
w.coalesced.view = &e2 w.coalesced.view = &e2
case ConfigEvent: case ConfigEvent:
w.decorations.Decorations.Maximized = e2.Config.Mode == Maximized
wasFocused := w.decorations.Config.Focused wasFocused := w.decorations.Config.Focused
w.decorations.Config = e2.Config w.decorations.Config = e2.Config
e2.Config = w.effectiveConfig() e2.Config = w.effectiveConfig()
@@ -721,7 +718,7 @@ func (w *Window) Event() event.Event {
if w.driver == nil { if w.driver == nil {
e, ok := w.nextEvent() e, ok := w.nextEvent()
if !ok { if !ok {
panic("window initialization failed without a DestroyEvent") panic("window initializion failed without a DestroyEvent")
} }
return e return e
} }
@@ -804,6 +801,7 @@ func (w *Window) decorate(e FrameEvent, o *op.Ops) image.Point {
default: default:
panic(fmt.Errorf("unknown WindowMode %v", m)) panic(fmt.Errorf("unknown WindowMode %v", m))
} }
deco.Perform(actions)
gtx := layout.Context{ gtx := layout.Context{
Ops: o, Ops: o,
Now: e.Now, Now: e.Now,
+1 -10
View File
@@ -30,15 +30,6 @@ func NewAffine2D(sx, hx, ox, hy, sy, oy float32) Affine2D {
} }
} }
// AffineId returns an identity transformation matrix that represents no transformation
// when applied.
func AffineId() Affine2D {
return NewAffine2D(
1, 0, 0,
0, 1, 0,
)
}
// Offset the transformation. // Offset the transformation.
func (a Affine2D) Offset(offset Point) Affine2D { func (a Affine2D) Offset(offset Point) Affine2D {
return Affine2D{ return Affine2D{
@@ -123,7 +114,7 @@ func (a Affine2D) Elems() (sx, hx, ox, hy, sy, oy float32) {
// Split a transform into two parts, one which is pure offset and the // Split a transform into two parts, one which is pure offset and the
// other representing the scaling, shearing and rotation part. // other representing the scaling, shearing and rotation part.
func (a Affine2D) Split() (srs Affine2D, offset Point) { func (a *Affine2D) Split() (srs Affine2D, offset Point) {
return Affine2D{ return Affine2D{
a: a.a, b: a.b, c: 0, a: a.a, b: a.b, c: 0,
d: a.d, e: a.e, f: 0, d: a.d, e: a.e, f: 0,
+30 -160
View File
@@ -27,11 +27,11 @@ func TestTransformOffset(t *testing.T) {
p := Point{X: 1, Y: 2} p := Point{X: 1, Y: 2}
o := Point{X: 2, Y: -3} o := Point{X: 2, Y: -3}
r := AffineId().Offset(o).Transform(p) r := Affine2D{}.Offset(o).Transform(p)
if !eq(r, Pt(3, -1)) { if !eq(r, Pt(3, -1)) {
t.Errorf("offset transformation mismatch: have %v, want {3 -1}", r) t.Errorf("offset transformation mismatch: have %v, want {3 -1}", r)
} }
i := AffineId().Offset(o).Invert().Transform(r) i := Affine2D{}.Offset(o).Invert().Transform(r)
if !eq(i, p) { if !eq(i, p) {
t.Errorf("offset transformation inverse mismatch: have %v, want %v", i, p) t.Errorf("offset transformation inverse mismatch: have %v, want %v", i, p)
} }
@@ -51,9 +51,6 @@ func TestString(t *testing.T) {
}, { }, {
in: NewAffine2D(29.142342, 31.4123412, 37.53152, 43.51324213, 47.123412, 53.14312342), in: NewAffine2D(29.142342, 31.4123412, 37.53152, 43.51324213, 47.123412, 53.14312342),
exp: "[[29.1423 31.4123 37.5315] [43.5132 47.1234 53.1431]]", exp: "[[29.1423 31.4123 37.5315] [43.5132 47.1234 53.1431]]",
}, {
in: AffineId(),
exp: "[[1 0 0] [0 1 0]]",
}, },
} }
for _, test := range tests { for _, test := range tests {
@@ -67,11 +64,11 @@ func TestTransformScale(t *testing.T) {
p := Point{X: 1, Y: 2} p := Point{X: 1, Y: 2}
s := Point{X: -1, Y: 2} s := Point{X: -1, Y: 2}
r := AffineId().Scale(Point{}, s).Transform(p) r := Affine2D{}.Scale(Point{}, s).Transform(p)
if !eq(r, Pt(-1, 4)) { if !eq(r, Pt(-1, 4)) {
t.Errorf("scale transformation mismatch: have %v, want {-1 4}", r) t.Errorf("scale transformation mismatch: have %v, want {-1 4}", r)
} }
i := AffineId().Scale(Point{}, s).Invert().Transform(r) i := Affine2D{}.Scale(Point{}, s).Invert().Transform(r)
if !eq(i, p) { if !eq(i, p) {
t.Errorf("scale transformation inverse mismatch: have %v, want %v", i, p) t.Errorf("scale transformation inverse mismatch: have %v, want %v", i, p)
} }
@@ -81,11 +78,11 @@ func TestTransformRotate(t *testing.T) {
p := Point{X: 1, Y: 0} p := Point{X: 1, Y: 0}
a := float32(math.Pi / 2) a := float32(math.Pi / 2)
r := AffineId().Rotate(Point{}, a).Transform(p) r := Affine2D{}.Rotate(Point{}, a).Transform(p)
if !eq(r, Pt(0, 1)) { if !eq(r, Pt(0, 1)) {
t.Errorf("rotate transformation mismatch: have %v, want {0 1}", r) t.Errorf("rotate transformation mismatch: have %v, want {0 1}", r)
} }
i := AffineId().Rotate(Point{}, a).Invert().Transform(r) i := Affine2D{}.Rotate(Point{}, a).Invert().Transform(r)
if !eq(i, p) { if !eq(i, p) {
t.Errorf("rotate transformation inverse mismatch: have %v, want %v", i, p) t.Errorf("rotate transformation inverse mismatch: have %v, want %v", i, p)
} }
@@ -94,11 +91,11 @@ func TestTransformRotate(t *testing.T) {
func TestTransformShear(t *testing.T) { func TestTransformShear(t *testing.T) {
p := Point{X: 1, Y: 1} p := Point{X: 1, Y: 1}
r := AffineId().Shear(Point{}, math.Pi/4, 0).Transform(p) r := Affine2D{}.Shear(Point{}, math.Pi/4, 0).Transform(p)
if !eq(r, Pt(2, 1)) { if !eq(r, Pt(2, 1)) {
t.Errorf("shear transformation mismatch: have %v, want {2 1}", r) t.Errorf("shear transformation mismatch: have %v, want {2 1}", r)
} }
i := AffineId().Shear(Point{}, math.Pi/4, 0).Invert().Transform(r) i := Affine2D{}.Shear(Point{}, math.Pi/4, 0).Invert().Transform(r)
if !eq(i, p) { if !eq(i, p) {
t.Errorf("shear transformation inverse mismatch: have %v, want %v", i, p) t.Errorf("shear transformation inverse mismatch: have %v, want %v", i, p)
} }
@@ -110,11 +107,11 @@ func TestTransformMultiply(t *testing.T) {
s := Point{X: -1, Y: 2} s := Point{X: -1, Y: 2}
a := float32(-math.Pi / 2) a := float32(-math.Pi / 2)
r := AffineId().Offset(o).Scale(Point{}, s).Rotate(Point{}, a).Shear(Point{}, math.Pi/4, 0).Transform(p) r := Affine2D{}.Offset(o).Scale(Point{}, s).Rotate(Point{}, a).Shear(Point{}, math.Pi/4, 0).Transform(p)
if !eq(r, Pt(1, 3)) { if !eq(r, Pt(1, 3)) {
t.Errorf("complex transformation mismatch: have %v, want {1 3}", r) t.Errorf("complex transformation mismatch: have %v, want {1 3}", r)
} }
i := AffineId().Offset(o).Scale(Point{}, s).Rotate(Point{}, a).Shear(Point{}, math.Pi/4, 0).Invert().Transform(r) i := Affine2D{}.Offset(o).Scale(Point{}, s).Rotate(Point{}, a).Shear(Point{}, math.Pi/4, 0).Invert().Transform(r)
if !eq(i, p) { if !eq(i, p) {
t.Errorf("complex transformation inverse mismatch: have %v, want %v", i, p) t.Errorf("complex transformation inverse mismatch: have %v, want %v", i, p)
} }
@@ -166,7 +163,7 @@ func TestPrimes(t *testing.T) {
func TestTransformScaleAround(t *testing.T) { func TestTransformScaleAround(t *testing.T) {
p := Pt(-1, -1) p := Pt(-1, -1)
target := Pt(-6, -13) target := Pt(-6, -13)
pt := AffineId().Scale(Pt(4, 5), Pt(2, 3)).Transform(p) pt := Affine2D{}.Scale(Pt(4, 5), Pt(2, 3)).Transform(p)
if !eq(pt, target) { if !eq(pt, target) {
t.Log(pt, "!=", target) t.Log(pt, "!=", target)
t.Error("Scale not as expected") t.Error("Scale not as expected")
@@ -175,7 +172,7 @@ func TestTransformScaleAround(t *testing.T) {
func TestTransformRotateAround(t *testing.T) { func TestTransformRotateAround(t *testing.T) {
p := Pt(-1, -1) p := Pt(-1, -1)
pt := AffineId().Rotate(Pt(1, 1), -math.Pi/2).Transform(p) pt := Affine2D{}.Rotate(Pt(1, 1), -math.Pi/2).Transform(p)
target := Pt(-1, 3) target := Pt(-1, 3)
if !eq(pt, target) { if !eq(pt, target) {
t.Log(pt, "!=", target) t.Log(pt, "!=", target)
@@ -184,12 +181,12 @@ func TestTransformRotateAround(t *testing.T) {
} }
func TestMulOrder(t *testing.T) { func TestMulOrder(t *testing.T) {
A := AffineId().Offset(Pt(100, 100)) A := Affine2D{}.Offset(Pt(100, 100))
B := AffineId().Scale(Point{}, Pt(2, 2)) B := Affine2D{}.Scale(Point{}, Pt(2, 2))
_ = A _ = A
_ = B _ = B
T1 := AffineId().Offset(Pt(100, 100)).Scale(Point{}, Pt(2, 2)) T1 := Affine2D{}.Offset(Pt(100, 100)).Scale(Point{}, Pt(2, 2))
T2 := B.Mul(A) T2 := B.Mul(A)
if T1 != T2 { if T1 != T2 {
@@ -202,9 +199,9 @@ func TestMulOrder(t *testing.T) {
func BenchmarkTransformOffset(b *testing.B) { func BenchmarkTransformOffset(b *testing.B) {
p := Point{X: 1, Y: 2} p := Point{X: 1, Y: 2}
o := Point{X: 0.5, Y: 0.5} o := Point{X: 0.5, Y: 0.5}
aff := AffineId().Offset(o) aff := Affine2D{}.Offset(o)
for b.Loop() { for i := 0; i < b.N; i++ {
p = aff.Transform(p) p = aff.Transform(p)
} }
_ = p _ = p
@@ -213,8 +210,8 @@ func BenchmarkTransformOffset(b *testing.B) {
func BenchmarkTransformScale(b *testing.B) { func BenchmarkTransformScale(b *testing.B) {
p := Point{X: 1, Y: 2} p := Point{X: 1, Y: 2}
s := Point{X: 0.5, Y: 0.5} s := Point{X: 0.5, Y: 0.5}
aff := AffineId().Scale(Point{}, s) aff := Affine2D{}.Scale(Point{}, s)
for b.Loop() { for i := 0; i < b.N; i++ {
p = aff.Transform(p) p = aff.Transform(p)
} }
_ = p _ = p
@@ -223,163 +220,36 @@ func BenchmarkTransformScale(b *testing.B) {
func BenchmarkTransformRotate(b *testing.B) { func BenchmarkTransformRotate(b *testing.B) {
p := Point{X: 1, Y: 2} p := Point{X: 1, Y: 2}
a := float32(math.Pi / 2) a := float32(math.Pi / 2)
aff := AffineId().Rotate(Point{}, a) aff := Affine2D{}.Rotate(Point{}, a)
for b.Loop() { for i := 0; i < b.N; i++ {
p = aff.Transform(p) p = aff.Transform(p)
} }
_ = p _ = p
} }
func BenchmarkTransformTranslateMultiply(b *testing.B) { func BenchmarkTransformTranslateMultiply(b *testing.B) {
a := AffineId().Offset(Point{X: 1, Y: 1}).Rotate(Point{}, math.Pi/3) a := Affine2D{}.Offset(Point{X: 1, Y: 1}).Rotate(Point{}, math.Pi/3)
t := AffineId().Offset(Point{X: 0.5, Y: 0.5}) t := Affine2D{}.Offset(Point{X: 0.5, Y: 0.5})
for b.Loop() { for i := 0; i < b.N; i++ {
a = a.Mul(t) a = a.Mul(t)
} }
} }
func BenchmarkTransformScaleMultiply(b *testing.B) { func BenchmarkTransformScaleMultiply(b *testing.B) {
a := AffineId().Offset(Point{X: 1, Y: 1}).Rotate(Point{}, math.Pi/3) a := Affine2D{}.Offset(Point{X: 1, Y: 1}).Rotate(Point{}, math.Pi/3)
t := AffineId().Offset(Point{X: 0.5, Y: 0.5}).Scale(Point{}, Point{X: 0.4, Y: -0.5}) t := Affine2D{}.Offset(Point{X: 0.5, Y: 0.5}).Scale(Point{}, Point{X: 0.4, Y: -0.5})
for b.Loop() { for i := 0; i < b.N; i++ {
a = a.Mul(t) a = a.Mul(t)
} }
} }
func BenchmarkTransformMultiply(b *testing.B) { func BenchmarkTransformMultiply(b *testing.B) {
a := AffineId().Offset(Point{X: 1, Y: 1}).Rotate(Point{}, math.Pi/3) a := Affine2D{}.Offset(Point{X: 1, Y: 1}).Rotate(Point{}, math.Pi/3)
t := AffineId().Offset(Point{X: 0.5, Y: 0.5}).Rotate(Point{}, math.Pi/7) t := Affine2D{}.Offset(Point{X: 0.5, Y: 0.5}).Rotate(Point{}, math.Pi/7)
for b.Loop() { for i := 0; i < b.N; i++ {
a = a.Mul(t) a = a.Mul(t)
} }
} }
func TestNewAffine2D(t *testing.T) {
tests := []struct {
sx, hx, ox, hy, sy, oy float32
expected Affine2D
}{
{1, 0, 0, 0, 1, 0, AffineId()},
{2, 0, 5, 0, 3, 7, Affine2D{a: 1, b: 0, c: 5, d: 0, e: 2, f: 7}},
{-1, 2, 3, 4, -5, 6, Affine2D{a: -2, b: 2, c: 3, d: 4, e: -6, f: 6}},
}
for i, test := range tests {
got := NewAffine2D(test.sx, test.hx, test.ox, test.hy, test.sy, test.oy)
if !eqaff(got, test.expected) {
t.Errorf(
"Test %d: NewAffine2D(%v, %v, %v, %v, %v, %v) = %v, want %v",
i, test.sx, test.hx, test.ox, test.hy, test.sy, test.oy, got, test.expected,
)
}
}
}
func TestAffineId(t *testing.T) {
id := AffineId()
testPoints := []Point{
{0, 0},
{1, 0},
{0, 1},
{-1, -1},
{10, 20},
}
for _, p := range testPoints {
transformed := id.Transform(p)
if !eq(transformed, p) {
t.Errorf("Identity transform changed point: %v -> %v", p, transformed)
}
}
}
func TestElems(t *testing.T) {
tests := []struct {
aff Affine2D
sx, hx, ox, hy, sy, oy float32
}{
{AffineId(), 1, 0, 0, 0, 1, 0},
{Affine2D{a: 1, b: 2, c: 3, d: 4, e: 5, f: 6}, 2, 2, 3, 4, 6, 6},
{NewAffine2D(7, 8, 9, 10, 11, 12), 7, 8, 9, 10, 11, 12},
}
for i, test := range tests {
sx, hx, ox, hy, sy, oy := test.aff.Elems()
if sx != test.sx || hx != test.hx || ox != test.ox ||
hy != test.hy || sy != test.sy || oy != test.oy {
t.Errorf(
"Test %d: %v.Elems() = (%v, %v, %v, %v, %v, %v), want (%v, %v, %v, %v, %v, %v)",
i, test.aff, sx, hx, ox, hy, sy, oy, test.sx, test.hx, test.ox, test.hy, test.sy, test.oy,
)
}
}
}
func TestSplit(t *testing.T) {
tests := []struct {
aff Affine2D
expectedSRS Affine2D
expectedOffset Point
}{
{
AffineId(),
AffineId(),
Point{0, 0},
},
{
Affine2D{a: 1, b: 2, c: 3, d: 4, e: 5, f: 6},
Affine2D{a: 1, b: 2, c: 0, d: 4, e: 5, f: 0},
Point{3, 6},
},
{
NewAffine2D(2, 0, 10, 0, 3, 20),
NewAffine2D(2, 0, 0, 0, 3, 0),
Point{10, 20},
},
}
for i, test := range tests {
srs, offset := test.aff.Split()
if !eqaff(srs, test.expectedSRS) || !eq(offset, test.expectedOffset) {
t.Errorf(
"Test %d: %v.Split() = (%v, %v), want (%v, %v)",
i, test.aff, srs, offset, test.expectedSRS, test.expectedOffset,
)
}
}
}
func TestShear(t *testing.T) {
p := Pt(2, 3)
origin := Pt(1, 1)
shearX := AffineId().Shear(origin, math.Pi/4, 0)
resultX := shearX.Transform(p)
expectedX := Pt(4, 3)
if !eq(resultX, expectedX) {
t.Errorf("Shear around origin in X: got %v, want %v", resultX, expectedX)
}
inverseX := shearX.Invert().Transform(resultX)
if !eq(inverseX, p) {
t.Errorf("Inverse shear X: got %v, want %v", inverseX, p)
}
shearY := AffineId().Shear(origin, 0, math.Pi/4)
resultY := shearY.Transform(p)
expectedY := Pt(2, 4)
if !eq(resultY, expectedY) {
t.Errorf("Shear around origin in Y: got %v, want %v", resultY, expectedY)
}
inverseY := shearY.Invert().Transform(resultY)
if !eq(inverseY, p) {
t.Errorf("Inverse shear Y: got %v, want %v", inverseY, p)
}
}
Generated
+76 -17
View File
@@ -1,25 +1,87 @@
{ {
"nodes": { "nodes": {
"android": {
"inputs": {
"devshell": "devshell",
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1701721028,
"narHash": "sha256-2z4YrdHPLoMZNWR1MPOjNZMqPg057i1eZXaYI6RTahQ=",
"owner": "tadfisher",
"repo": "android-nixpkgs",
"rev": "c923f9ec0f4dd0d7dc725dc5b73fbf03658e50dd",
"type": "github"
},
"original": {
"owner": "tadfisher",
"repo": "android-nixpkgs",
"type": "github"
}
},
"devshell": {
"inputs": {
"nixpkgs": [
"android",
"nixpkgs"
],
"systems": "systems"
},
"locked": {
"lastModified": 1701697687,
"narHash": "sha256-dLLE5wQBVv+pIb4bWmKFSw2DvLVyuEk0F7ng6hpZPSU=",
"owner": "numtide",
"repo": "devshell",
"rev": "c3bd77911391eb1638af6ce773de86da57ee6df5",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "devshell",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1701680307,
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1747953325, "lastModified": 1701282334,
"narHash": "sha256-y2ZtlIlNTuVJUZCqzZAhIw5rrKP4DOSklev6c8PyCkQ=", "narHash": "sha256-MxCVrXY6v4QmfTwIysjjaX0XUhqBbxTWWB4HXtDYsdk=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "55d1f923c480dadce40f5231feb472e81b0bab48", "rev": "057f9aecfb71c4437d2b27d3323df7f93c010b7e",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "NixOS",
"ref": "nixos-25.05", "ref": "23.11",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }
}, },
"root": { "root": {
"inputs": { "inputs": {
"nixpkgs": "nixpkgs", "android": "android",
"utils": "utils" "nixpkgs": "nixpkgs"
} }
}, },
"systems": { "systems": {
@@ -37,21 +99,18 @@
"type": "github" "type": "github"
} }
}, },
"utils": { "systems_2": {
"inputs": {
"systems": "systems"
},
"locked": { "locked": {
"lastModified": 1731533236, "lastModified": 1681028828,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "numtide", "owner": "nix-systems",
"repo": "flake-utils", "repo": "default",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "numtide", "owner": "nix-systems",
"repo": "flake-utils", "repo": "default",
"type": "github" "type": "github"
} }
} }
+46 -37
View File
@@ -3,38 +3,42 @@
description = "Gio build environment"; description = "Gio build environment";
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05"; nixpkgs.url = "github:NixOS/nixpkgs/23.11";
utils.url = "github:numtide/flake-utils"; android.url = "github:tadfisher/android-nixpkgs";
android.inputs.nixpkgs.follows = "nixpkgs";
}; };
outputs = { self, nixpkgs, utils }: outputs = { self, nixpkgs, android }:
utils.lib.eachDefaultSystem (system: let
let supportedSystems = [ "x86_64-linux" "x86_64-darwin" "aarch64-darwin" ];
pkgs = import nixpkgs { forAllSystems = f: nixpkgs.lib.genAttrs supportedSystems (system: f system);
inherit system; in
{
# allow unfree Android packages. devShells = forAllSystems
config.allowUnfree = true; (system:
# accept the Android SDK license. let
config.android_sdk.accept_license = true; pkgs = import nixpkgs {
}; inherit system;
in {
devShells = let
android-sdk = let
androidComposition = pkgs.androidenv.composeAndroidPackages {
platformVersions = [ "latest" ];
abiVersions = [ "armeabi-v7a" "arm64-v8a" ];
# Omit the deprecated tools package.
toolsVersion = null;
includeNDK = true;
}; };
in androidComposition.androidsdk; android-sdk = android.sdk.${system} (sdkPkgs: with sdkPkgs;
in { [
default = with pkgs; build-tools-31-0-0
mkShell (rec { cmdline-tools-latest
ANDROID_HOME = "${android-sdk}/libexec/android-sdk"; platform-tools
packages = [ android-sdk jdk clang ] platforms-android-31
++ (if stdenv.isLinux then [ ndk-bundle
]);
in
{
default = with pkgs; mkShell
({
ANDROID_SDK_ROOT = "${android-sdk}/share/android-sdk";
JAVA_HOME = jdk17.home;
packages = [
android-sdk
jdk17
clang
] ++ (if stdenv.isLinux then [
vulkan-headers vulkan-headers
libxkbcommon libxkbcommon
wayland wayland
@@ -43,12 +47,17 @@
xorg.libXfixes xorg.libXfixes
libGL libGL
pkg-config pkg-config
] else ] else if stdenv.isDarwin then [
[ ]); darwin.apple_sdk_11_0.frameworks.Foundation
} // (if stdenv.isLinux then { darwin.apple_sdk_11_0.frameworks.Metal
LD_LIBRARY_PATH = "${vulkan-loader}/lib"; darwin.apple_sdk_11_0.frameworks.QuartzCore
} else darwin.apple_sdk_11_0.frameworks.AppKit
{ })); darwin.apple_sdk_11_0.MacOSX-SDK
}; ] else [ ]);
}); } // (if stdenv.isLinux then {
LD_LIBRARY_PATH = "${vulkan-loader}/lib";
} else { }));
}
);
};
} }
+1 -1
View File
@@ -34,7 +34,7 @@ type Font struct {
// Face is an opaque handle to a typeface. The concrete implementation depends // Face is an opaque handle to a typeface. The concrete implementation depends
// upon the kind of font and shaper in use. // upon the kind of font and shaper in use.
type Face interface { type Face interface {
Face() *font.Face Face() font.Face
} }
// Typeface identifies a list of font families to attempt to use for displaying // Typeface identifies a list of font families to attempt to use for displaying
+43 -41
View File
@@ -16,21 +16,23 @@ import (
_ "image/png" _ "image/png"
giofont "gioui.org/font" giofont "gioui.org/font"
fontapi "github.com/go-text/typesetting/font" "github.com/go-text/typesetting/font"
"github.com/go-text/typesetting/font/opentype" fontapi "github.com/go-text/typesetting/opentype/api/font"
"github.com/go-text/typesetting/opentype/api/metadata"
"github.com/go-text/typesetting/opentype/loader"
) )
// Face is a thread-safe representation of a loaded font. For efficiency, applications // Face is a thread-safe representation of a loaded font. For efficiency, applications
// should construct a face for any given font file once, reusing it across different // should construct a face for any given font file once, reusing it across different
// text shapers. // text shapers.
type Face struct { type Face struct {
face *fontapi.Font face font.Font
font giofont.Font font giofont.Font
} }
// Parse constructs a Face from source bytes. // Parse constructs a Face from source bytes.
func Parse(src []byte) (Face, error) { func Parse(src []byte) (Face, error) {
ld, err := opentype.NewLoader(bytes.NewReader(src)) ld, err := loader.NewLoader(bytes.NewReader(src))
if err != nil { if err != nil {
return Face{}, err return Face{}, err
} }
@@ -47,11 +49,11 @@ func Parse(src []byte) (Face, error) {
// ParseCollection parse an Opentype font file, with support for collections. // ParseCollection parse an Opentype font file, with support for collections.
// Single font files are supported, returning a slice with length 1. // Single font files are supported, returning a slice with length 1.
// The returned fonts are automatically wrapped in a text.FontFace with // The returned fonts are automatically wrapped in a text.FontFace with
// inferred font font. // inferred font metadata.
// BUG(whereswaldon): the only Variant that can be detected automatically is // BUG(whereswaldon): the only Variant that can be detected automatically is
// "Mono". // "Mono".
func ParseCollection(src []byte) ([]giofont.FontFace, error) { func ParseCollection(src []byte) ([]giofont.FontFace, error) {
lds, err := opentype.NewLoaders(bytes.NewReader(src)) lds, err := loader.NewLoaders(bytes.NewReader(src))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -74,7 +76,7 @@ func ParseCollection(src []byte) ([]giofont.FontFace, error) {
return out, nil return out, nil
} }
func DescriptionToFont(md fontapi.Description) giofont.Font { func DescriptionToFont(md metadata.Description) giofont.Font {
return giofont.Font{ return giofont.Font{
Typeface: giofont.Typeface(md.Family), Typeface: giofont.Typeface(md.Family),
Style: gioStyle(md.Aspect.Style), Style: gioStyle(md.Aspect.Style),
@@ -82,30 +84,30 @@ func DescriptionToFont(md fontapi.Description) giofont.Font {
} }
} }
func FontToDescription(font giofont.Font) fontapi.Description { func FontToDescription(font giofont.Font) metadata.Description {
return fontapi.Description{ return metadata.Description{
Family: string(font.Typeface), Family: string(font.Typeface),
Aspect: fontapi.Aspect{ Aspect: metadata.Aspect{
Style: mdStyle(font.Style), Style: mdStyle(font.Style),
Weight: mdWeight(font.Weight), Weight: mdWeight(font.Weight),
}, },
} }
} }
// parseLoader parses the contents of the loader into a face and its font. // parseLoader parses the contents of the loader into a face and its metadata.
func parseLoader(ld *opentype.Loader) (*fontapi.Font, giofont.Font, error) { func parseLoader(ld *loader.Loader) (font.Font, giofont.Font, error) {
ft, err := fontapi.NewFont(ld) ft, err := fontapi.NewFont(ld)
if err != nil { if err != nil {
return nil, giofont.Font{}, err return nil, giofont.Font{}, err
} }
data := DescriptionToFont(ft.Describe()) data := DescriptionToFont(metadata.Metadata(ld))
return ft, data, nil return ft, data, nil
} }
// Face returns a thread-unsafe wrapper for this Face suitable for use by a single shaper. // Face returns a thread-unsafe wrapper for this Face suitable for use by a single shaper.
// Face many be invoked any number of times and is safe so long as each return value is // Face many be invoked any number of times and is safe so long as each return value is
// only used by one goroutine. // only used by one goroutine.
func (f Face) Face() *fontapi.Face { func (f Face) Face() font.Face {
return &fontapi.Face{Font: f.face} return &fontapi.Face{Font: f.face}
} }
@@ -117,74 +119,74 @@ func (f Face) Font() giofont.Font {
return f.font return f.font
} }
func gioStyle(s fontapi.Style) giofont.Style { func gioStyle(s metadata.Style) giofont.Style {
switch s { switch s {
case fontapi.StyleItalic: case metadata.StyleItalic:
return giofont.Italic return giofont.Italic
case fontapi.StyleNormal: case metadata.StyleNormal:
fallthrough fallthrough
default: default:
return giofont.Regular return giofont.Regular
} }
} }
func mdStyle(g giofont.Style) fontapi.Style { func mdStyle(g giofont.Style) metadata.Style {
switch g { switch g {
case giofont.Italic: case giofont.Italic:
return fontapi.StyleItalic return metadata.StyleItalic
case giofont.Regular: case giofont.Regular:
fallthrough fallthrough
default: default:
return fontapi.StyleNormal return metadata.StyleNormal
} }
} }
func gioWeight(w fontapi.Weight) giofont.Weight { func gioWeight(w metadata.Weight) giofont.Weight {
switch w { switch w {
case fontapi.WeightThin: case metadata.WeightThin:
return giofont.Thin return giofont.Thin
case fontapi.WeightExtraLight: case metadata.WeightExtraLight:
return giofont.ExtraLight return giofont.ExtraLight
case fontapi.WeightLight: case metadata.WeightLight:
return giofont.Light return giofont.Light
case fontapi.WeightNormal: case metadata.WeightNormal:
return giofont.Normal return giofont.Normal
case fontapi.WeightMedium: case metadata.WeightMedium:
return giofont.Medium return giofont.Medium
case fontapi.WeightSemibold: case metadata.WeightSemibold:
return giofont.SemiBold return giofont.SemiBold
case fontapi.WeightBold: case metadata.WeightBold:
return giofont.Bold return giofont.Bold
case fontapi.WeightExtraBold: case metadata.WeightExtraBold:
return giofont.ExtraBold return giofont.ExtraBold
case fontapi.WeightBlack: case metadata.WeightBlack:
return giofont.Black return giofont.Black
default: default:
return giofont.Normal return giofont.Normal
} }
} }
func mdWeight(g giofont.Weight) fontapi.Weight { func mdWeight(g giofont.Weight) metadata.Weight {
switch g { switch g {
case giofont.Thin: case giofont.Thin:
return fontapi.WeightThin return metadata.WeightThin
case giofont.ExtraLight: case giofont.ExtraLight:
return fontapi.WeightExtraLight return metadata.WeightExtraLight
case giofont.Light: case giofont.Light:
return fontapi.WeightLight return metadata.WeightLight
case giofont.Normal: case giofont.Normal:
return fontapi.WeightNormal return metadata.WeightNormal
case giofont.Medium: case giofont.Medium:
return fontapi.WeightMedium return metadata.WeightMedium
case giofont.SemiBold: case giofont.SemiBold:
return fontapi.WeightSemibold return metadata.WeightSemibold
case giofont.Bold: case giofont.Bold:
return fontapi.WeightBold return metadata.WeightBold
case giofont.ExtraBold: case giofont.ExtraBold:
return fontapi.WeightExtraBold return metadata.WeightExtraBold
case giofont.Black: case giofont.Black:
return fontapi.WeightBlack return metadata.WeightBlack
default: default:
return fontapi.WeightNormal return metadata.WeightNormal
} }
} }
+2 -9
View File
@@ -322,8 +322,6 @@ func (s *Scroll) Update(cfg unit.Metric, q input.Source, t time.Time, axis Axis,
s.scroll += e.Scroll.X s.scroll += e.Scroll.X
case Vertical: case Vertical:
s.scroll += e.Scroll.Y s.scroll += e.Scroll.Y
case Both:
s.scroll += e.Scroll.X + e.Scroll.Y
} }
iscroll := int(s.scroll) iscroll := int(s.scroll)
s.scroll -= float32(iscroll) s.scroll -= float32(iscroll)
@@ -355,15 +353,10 @@ func (s *Scroll) Update(cfg unit.Metric, q input.Source, t time.Time, axis Axis,
} }
func (s *Scroll) val(axis Axis, p f32.Point) float32 { func (s *Scroll) val(axis Axis, p f32.Point) float32 {
switch axis { if axis == Horizontal {
case Horizontal:
return p.X return p.X
case Vertical: } else {
return p.Y return p.Y
case Both:
return p.X + p.Y
default:
return 0
} }
} }
+9 -7
View File
@@ -1,14 +1,16 @@
module gioui.org module gioui.org
go 1.23.8 go 1.21
require ( require (
eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d
gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2
gioui.org/shader v1.0.8 gioui.org/shader v1.0.8
github.com/go-text/typesetting v0.3.0 github.com/go-text/typesetting v0.1.1
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 golang.org/x/exp v0.0.0-20221012211006-4de253d81b95
golang.org/x/exp/shiny v0.0.0-20250408133849-7e4ce0ab07d0 golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91
golang.org/x/image v0.26.0 golang.org/x/image v0.5.0
golang.org/x/sys v0.33.0 golang.org/x/sys v0.5.0
golang.org/x/text v0.24.0
) )
require golang.org/x/text v0.9.0
+40 -14
View File
@@ -1,19 +1,45 @@
eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d h1:ARo7NCVvN2NdhLlJE9xAbKweuI9L6UgfTbYb0YwPacY= eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d h1:ARo7NCVvN2NdhLlJE9xAbKweuI9L6UgfTbYb0YwPacY=
eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d/go.mod h1:OYVuxibdk9OSLX8vAqydtRPP87PyTFcT9uH3MlEGBQA= eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d/go.mod h1:OYVuxibdk9OSLX8vAqydtRPP87PyTFcT9uH3MlEGBQA=
gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ= gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2 h1:AGDDxsJE1RpcXTAxPG2B4jrwVUJGFDjINIPi1jtO6pc=
gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
gioui.org/shader v1.0.8 h1:6ks0o/A+b0ne7RzEqRZK5f4Gboz2CfG+mVliciy6+qA= gioui.org/shader v1.0.8 h1:6ks0o/A+b0ne7RzEqRZK5f4Gboz2CfG+mVliciy6+qA=
gioui.org/shader v1.0.8/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM= gioui.org/shader v1.0.8/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM=
github.com/go-text/typesetting v0.3.0 h1:OWCgYpp8njoxSRpwrdd1bQOxdjOXDj9Rqart9ML4iF4= github.com/go-text/typesetting v0.1.1 h1:bGAesCuo85nXnEN5LmFMVGAGpGkCPtHrZLi//qD7EJo=
github.com/go-text/typesetting v0.3.0/go.mod h1:qjZLkhRgOEYMhU9eHBr3AR4sfnGJvOXNLt8yRAySFuY= github.com/go-text/typesetting v0.1.1/go.mod h1:d22AnmeKq/on0HNv73UFriMKc4Ez6EqZAofLhAzpSzI=
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066 h1:qCuYC+94v2xrb1PoS4NIDe7DGYtLnU2wWiQe9a1B1c0= github.com/go-text/typesetting-utils v0.0.0-20231211103740-d9332ae51f04 h1:zBx+p/W2aQYtNuyZNcTfinWvXBQwYtDfme051PR/lAY=
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o= github.com/go-text/typesetting-utils v0.0.0-20231211103740-d9332ae51f04/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/exp/shiny v0.0.0-20250408133849-7e4ce0ab07d0 h1:tMSqXTK+AQdW3LpCbfatHSRPHeW6+2WuxaVQuHftn80= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp/shiny v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:ygj7T6vSGhhm/9yTpOQQNvuAUFziTH7RUiH74EoE2C8= golang.org/x/exp v0.0.0-20221012211006-4de253d81b95 h1:sBdrWpxhGDdTAYNqbgBLAR+ULAPPhfgncLr1X0lyWtg=
golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY= golang.org/x/exp v0.0.0-20221012211006-4de253d81b95/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c= golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91 h1:ryT6Nf0R83ZgD8WnFFdfI8wCeyqgdXWN4+CkFVNPAT0=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91/go.mod h1:VjAR7z0ngyATZTELrBSkxOOHhhlnVUxDye4mcjx5h/8=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+3 -2
View File
@@ -4,7 +4,8 @@ package gpu
import ( import (
"fmt" "fmt"
"image"
"gioui.org/internal/f32"
) )
type textureCacheKey struct { type textureCacheKey struct {
@@ -35,7 +36,7 @@ type opCache struct {
type opCacheValue struct { type opCacheValue struct {
data pathData data pathData
bounds image.Rectangle bounds f32.Rectangle
// the fields below are handled by opCache // the fields below are handled by opCache
key opKey key opKey
keep bool keep bool
+6 -6
View File
@@ -19,10 +19,10 @@ type quadSplitter struct {
func encodeQuadTo(data []byte, meta uint32, from, ctrl, to f32.Point) { func encodeQuadTo(data []byte, meta uint32, from, ctrl, to f32.Point) {
// inlined code: // inlined code:
// encodeVertex(data, meta, 1, -1, from, ctrl, to) // encodeVertex(data, meta, -1, 1, from, ctrl, to)
// encodeVertex(data[vertStride:], meta, 1, 1, from, ctrl, to) // encodeVertex(data[vertStride:], meta, 1, 1, from, ctrl, to)
// encodeVertex(data[vertStride*2:], meta, -1, -1, from, ctrl, to) // encodeVertex(data[vertStride*2:], meta, -1, -1, from, ctrl, to)
// encodeVertex(data[vertStride*3:], meta, -1, 1, from, ctrl, to) // encodeVertex(data[vertStride*3:], meta, 1, -1, from, ctrl, to)
// this code needs to stay in sync with `vertex.encode`. // this code needs to stay in sync with `vertex.encode`.
bo := binary.LittleEndian bo := binary.LittleEndian
@@ -48,10 +48,10 @@ func encodeQuadTo(data []byte, meta uint32, from, ctrl, to f32.Point) {
} }
const ( const (
nwCorner = 1*0.5 + 0*0.25 nwCorner = 1*0.25 + 0*0.5
neCorner = 1*0.5 + 1*0.25 neCorner = 1*0.25 + 1*0.5
swCorner = 0*0.5 + 0*0.25 swCorner = 0*0.25 + 0*0.5
seCorner = 0*0.5 + 1*0.25 seCorner = 0*0.25 + 1*0.5
) )
func encodeVertex(data []byte, meta uint32, cornerx, cornery int16, from, ctrl, to f32.Point) { func encodeVertex(data []byte, meta uint32, cornerx, cornery int16, from, ctrl, to f32.Point) {
+1 -1
View File
@@ -10,7 +10,7 @@ import (
func BenchmarkEncodeQuadTo(b *testing.B) { func BenchmarkEncodeQuadTo(b *testing.B) {
var data [vertStride * 4]byte var data [vertStride * 4]byte
for i := 0; b.Loop(); i++ { for i := 0; i < b.N; i++ {
v := float32(i) v := float32(i)
encodeQuadTo(data[:], 123, encodeQuadTo(data[:], 123,
f32.Point{X: v, Y: v}, f32.Point{X: v, Y: v},
+2193
View File
File diff suppressed because it is too large Load Diff
+129
View File
@@ -0,0 +1,129 @@
// SPDX-License-Identifier: Unlicense OR MIT
package gpu
import (
"unsafe"
"gioui.org/cpu"
)
// This file contains code specific to running compute shaders on the CPU.
// dispatcher dispatches CPU compute programs across multiple goroutines.
type dispatcher struct {
// done is notified when a worker completes its work slice.
done chan struct{}
// work receives work slice indices. It is closed when the dispatcher is released.
work chan work
// dispatch receives compute jobs, which is then split among workers.
dispatch chan dispatch
// sync receives notification when a Sync completes.
sync chan struct{}
}
type work struct {
ctx *cpu.DispatchContext
index int
}
type dispatch struct {
_type jobType
program *cpu.ProgramInfo
descSet unsafe.Pointer
x, y, z int
}
type jobType uint8
const (
jobDispatch jobType = iota
jobBarrier
jobSync
)
func newDispatcher(workers int) *dispatcher {
d := &dispatcher{
work: make(chan work, workers),
done: make(chan struct{}, workers),
// Leave some room to avoid blocking calls to Dispatch.
dispatch: make(chan dispatch, 20),
sync: make(chan struct{}),
}
for i := 0; i < workers; i++ {
go d.worker()
}
go d.dispatcher()
return d
}
func (d *dispatcher) dispatcher() {
defer close(d.work)
var free []*cpu.DispatchContext
defer func() {
for _, ctx := range free {
ctx.Free()
}
}()
var used []*cpu.DispatchContext
for job := range d.dispatch {
switch job._type {
case jobDispatch:
if len(free) == 0 {
free = append(free, cpu.NewDispatchContext())
}
ctx := free[len(free)-1]
free = free[:len(free)-1]
used = append(used, ctx)
ctx.Prepare(cap(d.work), job.program, job.descSet, job.x, job.y, job.z)
for i := 0; i < cap(d.work); i++ {
d.work <- work{
ctx: ctx,
index: i,
}
}
case jobBarrier:
// Wait for all outstanding dispatches to complete.
for i := 0; i < len(used)*cap(d.work); i++ {
<-d.done
}
free = append(free, used...)
used = used[:0]
case jobSync:
d.sync <- struct{}{}
}
}
}
func (d *dispatcher) worker() {
thread := cpu.NewThreadContext()
defer thread.Free()
for w := range d.work {
w.ctx.Dispatch(w.index, thread)
d.done <- struct{}{}
}
}
func (d *dispatcher) Barrier() {
d.dispatch <- dispatch{_type: jobBarrier}
}
func (d *dispatcher) Sync() {
d.dispatch <- dispatch{_type: jobSync}
<-d.sync
}
func (d *dispatcher) Dispatch(program *cpu.ProgramInfo, descSet unsafe.Pointer, x, y, z int) {
d.dispatch <- dispatch{
_type: jobDispatch,
program: program,
descSet: descSet,
x: x,
y: y,
z: z,
}
}
func (d *dispatcher) Stop() {
close(d.dispatch)
}
+81 -103
View File
@@ -9,13 +9,12 @@ package gpu
import ( import (
"encoding/binary" "encoding/binary"
"errors"
"fmt" "fmt"
"image" "image"
"image/color" "image/color"
"math" "math"
"os"
"reflect" "reflect"
"slices"
"time" "time"
"unsafe" "unsafe"
@@ -118,17 +117,17 @@ type drawState struct {
} }
type pathOp struct { type pathOp struct {
off image.Point off f32.Point
// rect tracks whether the clip stack can be represented by a // rect tracks whether the clip stack can be represented by a
// pixel-aligned rectangle. // pixel-aligned rectangle.
rect bool rect bool
// clip is the union of all // clip is the union of all
// later clip rectangles. // later clip rectangles.
clip image.Rectangle clip image.Rectangle
bounds image.Rectangle bounds f32.Rectangle
// intersect is the intersection of bounds and all // intersect is the intersection of bounds and all
// previous clip bounds. // previous clip bounds.
intersect image.Rectangle intersect f32.Rectangle
pathKey opKey pathKey opKey
path bool path bool
pathVerts []byte pathVerts []byte
@@ -190,7 +189,7 @@ const (
// imageOpData is the shadow of paint.ImageOp. // imageOpData is the shadow of paint.ImageOp.
type imageOpData struct { type imageOpData struct {
src *image.RGBA src *image.RGBA
handle any handle interface{}
filter byte filter byte
} }
@@ -201,7 +200,7 @@ type linearGradientOpData struct {
color2 color.NRGBA color2 color.NRGBA
} }
func decodeImageOp(data []byte, refs []any) imageOpData { func decodeImageOp(data []byte, refs []interface{}) imageOpData {
handle := refs[1] handle := refs[1]
if handle == nil { if handle == nil {
return imageOpData{} return imageOpData{}
@@ -344,12 +343,13 @@ func New(api API) (GPU, error) {
func NewWithDevice(d driver.Device) (GPU, error) { func NewWithDevice(d driver.Device) (GPU, error) {
d.BeginFrame(nil, false, image.Point{}) d.BeginFrame(nil, false, image.Point{})
defer d.EndFrame() defer d.EndFrame()
forceCompute := os.Getenv("GIORENDERER") == "forcecompute"
feats := d.Caps().Features feats := d.Caps().Features
switch { switch {
case feats.Has(driver.FeatureFloatRenderTargets) && feats.Has(driver.FeatureSRGB): case !forceCompute && feats.Has(driver.FeatureFloatRenderTargets) && feats.Has(driver.FeatureSRGB):
return newGPU(d) return newGPU(d)
} }
return nil, errors.New("no available GPU driver") return newCompute(d)
} }
func newGPU(ctx driver.Device) (*gpu, error) { func newGPU(ctx driver.Device) (*gpu, error) {
@@ -548,7 +548,7 @@ func newBlitter(ctx driver.Device) *blitter {
b.texUniforms = new(blitTexUniforms) b.texUniforms = new(blitTexUniforms)
b.linearGradientUniforms = new(blitLinearGradientUniforms) b.linearGradientUniforms = new(blitLinearGradientUniforms)
pipelines, err := createColorPrograms(ctx, gio.Shader_blit_vert, gio.Shader_blit_frag, pipelines, err := createColorPrograms(ctx, gio.Shader_blit_vert, gio.Shader_blit_frag,
[3]any{b.colUniforms, b.linearGradientUniforms, b.texUniforms}, [3]interface{}{b.colUniforms, b.linearGradientUniforms, b.texUniforms},
) )
if err != nil { if err != nil {
panic(err) panic(err)
@@ -566,7 +566,7 @@ func (b *blitter) release() {
} }
} }
func createColorPrograms(b driver.Device, vsSrc shader.Sources, fsSrc [3]shader.Sources, uniforms [3]any) (pipelines [2][3]*pipeline, err error) { func createColorPrograms(b driver.Device, vsSrc shader.Sources, fsSrc [3]shader.Sources, uniforms [3]interface{}) (pipelines [2][3]*pipeline, err error) {
defer func() { defer func() {
if err != nil { if err != nil {
for _, p := range pipelines { for _, p := range pipelines {
@@ -823,7 +823,7 @@ func (r *renderer) packLayers(layers []opacityLayer) []opacityLayer {
layers[l.parent].clip = b.Union(l.clip) layers[l.parent].clip = b.Union(l.clip)
} }
if l.clip.Empty() { if l.clip.Empty() {
layers = slices.Delete(layers, i, i+1) layers = append(layers[:i], layers[i+1:]...)
} }
} }
// Pack layers. // Pack layers.
@@ -872,7 +872,7 @@ func (r *renderer) drawLayers(layers []opacityLayer, ops []imageOp) {
r.drawOps(true, l.clip.Min.Mul(-1), l.clip.Size(), ops[l.opStart:l.opEnd]) r.drawOps(true, l.clip.Min.Mul(-1), l.clip.Size(), ops[l.opStart:l.opEnd])
sr := f32.FRect(v) sr := f32.FRect(v)
uvScale, uvOffset := texSpaceTransform(sr, f.size) uvScale, uvOffset := texSpaceTransform(sr, f.size)
uvTrans := f32.AffineId().Scale(f32.Point{}, uvScale).Offset(uvOffset) uvTrans := f32.Affine2D{}.Scale(f32.Point{}, uvScale).Offset(uvOffset)
// Replace layer ops with one textured op. // Replace layer ops with one textured op.
ops[l.opStart] = imageOp{ ops[l.opStart] = imageOp{
clip: l.clip, clip: l.clip,
@@ -902,14 +902,16 @@ func (d *drawOps) reset(viewport image.Point) {
d.opacityStack = d.opacityStack[:0] d.opacityStack = d.opacityStack[:0]
} }
func (d *drawOps) collect(root *op.Ops, viewportSize image.Point) { func (d *drawOps) collect(root *op.Ops, viewport image.Point) {
viewport := image.Rectangle{Max: viewportSize} viewf := f32.Rectangle{
Max: f32.Point{X: float32(viewport.X), Y: float32(viewport.Y)},
}
var ops *ops.Ops var ops *ops.Ops
if root != nil { if root != nil {
ops = &root.Internal ops = &root.Internal
} }
d.reader.Reset(ops) d.reader.Reset(ops)
d.collectOps(&d.reader, viewport) d.collectOps(&d.reader, viewf)
} }
func (d *drawOps) buildPaths(ctx driver.Device) { func (d *drawOps) buildPaths(ctx driver.Device) {
@@ -930,7 +932,7 @@ func (d *drawOps) newPathOp() *pathOp {
return &d.pathOpCache[len(d.pathOpCache)-1] return &d.pathOpCache[len(d.pathOpCache)-1]
} }
func (d *drawOps) addClipPath(state *drawState, aux []byte, auxKey opKey, bounds image.Rectangle, off image.Point) { func (d *drawOps) addClipPath(state *drawState, aux []byte, auxKey opKey, bounds f32.Rectangle, off f32.Point) {
npath := d.newPathOp() npath := d.newPathOp()
*npath = pathOp{ *npath = pathOp{
parent: state.cpath, parent: state.cpath,
@@ -955,9 +957,7 @@ func (d *drawOps) addClipPath(state *drawState, aux []byte, auxKey opKey, bounds
func (d *drawOps) save(id int, state f32.Affine2D) { func (d *drawOps) save(id int, state f32.Affine2D) {
if extra := id - len(d.states) + 1; extra > 0 { if extra := id - len(d.states) + 1; extra > 0 {
for range extra { d.states = append(d.states, make([]f32.Affine2D, extra)...)
d.states = append(d.states, f32.AffineId())
}
} }
d.states[id] = state d.states[id] = state
} }
@@ -971,14 +971,13 @@ func (k opKey) SetTransform(t f32.Affine2D) opKey {
return k return k
} }
func (d *drawOps) collectOps(r *ops.Reader, viewport image.Rectangle) { func (d *drawOps) collectOps(r *ops.Reader, viewport f32.Rectangle) {
var quads quadsOp var (
state := drawState{ quads quadsOp
t: f32.AffineId(), state drawState
} )
reset := func() { reset := func() {
state = drawState{ state = drawState{
t: f32.AffineId(),
color: color.NRGBA{A: 0xff}, color: color.NRGBA{A: 0xff},
} }
} }
@@ -1033,8 +1032,8 @@ loop:
var op ops.ClipOp var op ops.ClipOp
op.Decode(encOp.Data) op.Decode(encOp.Data)
quads.key.outline = op.Outline quads.key.outline = op.Outline
bounds := op.Bounds bounds := f32.FRect(op.Bounds)
trans, off := transformOffset(state.t) trans, off := state.t.Split()
if len(quads.aux) > 0 { if len(quads.aux) > 0 {
// There is a clipping path, build the gpu data and update the // There is a clipping path, build the gpu data and update the
// cache key such that it will be equal only if the transform is the // cache key such that it will be equal only if the transform is the
@@ -1045,11 +1044,11 @@ loop:
// Why is this not used for the offset shapes? // Why is this not used for the offset shapes?
bounds = v.bounds bounds = v.bounds
} else { } else {
newPathData, newBounds := d.buildVerts( var pathData []byte
pathData, bounds = d.buildVerts(
quads.aux, trans, quads.key.outline, quads.key.strokeWidth, quads.aux, trans, quads.key.outline, quads.key.strokeWidth,
) )
quads.aux = newPathData quads.aux = pathData
bounds = newBounds.Round()
// add it to the cache, without GPU data, so the transform can be // add it to the cache, without GPU data, so the transform can be
// reused. // reused.
d.pathCache.put(quads.key, opCacheValue{bounds: bounds}) d.pathCache.put(quads.key, opCacheValue{bounds: bounds})
@@ -1057,7 +1056,6 @@ loop:
} else { } else {
quads.aux, bounds, _ = d.boundsForTransformedRect(bounds, trans) quads.aux, bounds, _ = d.boundsForTransformedRect(bounds, trans)
quads.key = opKey{Key: encOp.Key} quads.key = opKey{Key: encOp.Key}
quads.key = quads.key.SetTransform(trans)
} }
d.addClipPath(&state, quads.aux, quads.key, bounds, off) d.addClipPath(&state, quads.aux, quads.key, bounds, off)
quads = quadsOp{} quads = quadsOp{}
@@ -1081,21 +1079,21 @@ loop:
// Transform (if needed) the painting rectangle and if so generate a clip path, // Transform (if needed) the painting rectangle and if so generate a clip path,
// for those cases also compute a partialTrans that maps texture coordinates between // for those cases also compute a partialTrans that maps texture coordinates between
// the new bounding rectangle and the transformed original paint rectangle. // the new bounding rectangle and the transformed original paint rectangle.
t, off := transformOffset(state.t) t, off := state.t.Split()
// Fill the clip area, unless the material is a (bounded) image. // Fill the clip area, unless the material is a (bounded) image.
// TODO: Find a tighter bound. // TODO: Find a tighter bound.
inf := int(1e6) inf := float32(1e6)
dst := image.Rect(-inf, -inf, inf, inf) dst := f32.Rect(-inf, -inf, inf, inf)
if state.matType == materialTexture { if state.matType == materialTexture {
sz := state.image.src.Rect.Size() sz := state.image.src.Rect.Size()
dst = image.Rectangle{Max: sz} dst = f32.Rectangle{Max: layout.FPt(sz)}
} }
clipData, bnd, partialTrans := d.boundsForTransformedRect(dst, t) clipData, bnd, partialTrans := d.boundsForTransformedRect(dst, t)
bounds := viewport.Intersect(bnd.Add(off)) cl := viewport.Intersect(bnd.Add(off))
if state.cpath != nil { if state.cpath != nil {
bounds = state.cpath.intersect.Intersect(bounds) cl = state.cpath.intersect.Intersect(cl)
} }
if bounds.Empty() { if cl.Empty() {
continue continue
} }
@@ -1103,10 +1101,11 @@ loop:
// The paint operation is sheared or rotated, add a clip path representing // The paint operation is sheared or rotated, add a clip path representing
// this transformed rectangle. // this transformed rectangle.
k := opKey{Key: encOp.Key} k := opKey{Key: encOp.Key}
k = k.SetTransform(t) k.SetTransform(t) // TODO: This call has no effect.
d.addClipPath(&state, clipData, k, bnd, off) d.addClipPath(&state, clipData, k, bnd, off)
} }
bounds := cl.Round()
mat := state.materialFor(bnd, off, partialTrans, bounds) mat := state.materialFor(bnd, off, partialTrans, bounds)
rect := state.cpath == nil || state.cpath.rect rect := state.cpath == nil || state.cpath.rect
@@ -1160,10 +1159,9 @@ func expandPathOp(p *pathOp, clip image.Rectangle) {
} }
} }
func (d *drawState) materialFor(rect image.Rectangle, off image.Point, partTrans f32.Affine2D, clip image.Rectangle) material { func (d *drawState) materialFor(rect f32.Rectangle, off f32.Point, partTrans f32.Affine2D, clip image.Rectangle) material {
m := material{ m := material{
opacity: 1., opacity: 1.,
uvTrans: f32.AffineId(),
} }
switch d.matType { switch d.matType {
case materialColor: case materialColor:
@@ -1180,7 +1178,7 @@ func (d *drawState) materialFor(rect image.Rectangle, off image.Point, partTrans
m.uvTrans = partTrans.Mul(gradientSpaceTransform(clip, off, d.stop1, d.stop2)) m.uvTrans = partTrans.Mul(gradientSpaceTransform(clip, off, d.stop1, d.stop2))
case materialTexture: case materialTexture:
m.material = materialTexture m.material = materialTexture
dr := rect.Add(off) dr := rect.Add(off).Round()
sz := d.image.src.Bounds().Size() sz := d.image.src.Bounds().Size()
sr := f32.Rectangle{ sr := f32.Rectangle{
Max: f32.Point{ Max: f32.Point{
@@ -1197,7 +1195,7 @@ func (d *drawState) materialFor(rect image.Rectangle, off image.Point, partTrans
sr.Min.Y += float32(clip.Min.Y-dr.Min.Y) * sdy / dy sr.Min.Y += float32(clip.Min.Y-dr.Min.Y) * sdy / dy
sr.Max.Y -= float32(dr.Max.Y-clip.Max.Y) * sdy / dy sr.Max.Y -= float32(dr.Max.Y-clip.Max.Y) * sdy / dy
uvScale, uvOffset := texSpaceTransform(sr, sz) uvScale, uvOffset := texSpaceTransform(sr, sz)
m.uvTrans = partTrans.Mul(f32.AffineId().Scale(f32.Point{}, uvScale).Offset(uvOffset)) m.uvTrans = partTrans.Mul(f32.Affine2D{}.Scale(f32.Point{}, uvScale).Offset(uvOffset))
m.data = d.image m.data = d.image
} }
return m return m
@@ -1318,7 +1316,7 @@ func (b *blitter) blit(mat materialType, fbo bool, col f32color.RGBA, col1, col2
// newUniformBuffer creates a new GPU uniform buffer backed by the // newUniformBuffer creates a new GPU uniform buffer backed by the
// structure uniformBlock points to. // structure uniformBlock points to.
func newUniformBuffer(b driver.Device, uniformBlock any) *uniformBuffer { func newUniformBuffer(b driver.Device, uniformBlock interface{}) *uniformBuffer {
ref := reflect.ValueOf(uniformBlock) ref := reflect.ValueOf(uniformBlock)
// Determine the size of the uniforms structure, *uniforms. // Determine the size of the uniforms structure, *uniforms.
size := ref.Elem().Type().Size() size := ref.Elem().Type().Size()
@@ -1365,19 +1363,19 @@ func texSpaceTransform(r f32.Rectangle, bounds image.Point) (f32.Point, f32.Poin
} }
// gradientSpaceTransform transforms stop1 and stop2 to [(0,0), (1,1)]. // gradientSpaceTransform transforms stop1 and stop2 to [(0,0), (1,1)].
func gradientSpaceTransform(clip image.Rectangle, off image.Point, stop1, stop2 f32.Point) f32.Affine2D { func gradientSpaceTransform(clip image.Rectangle, off f32.Point, stop1, stop2 f32.Point) f32.Affine2D {
d := stop2.Sub(stop1) d := stop2.Sub(stop1)
l := float32(math.Sqrt(float64(d.X*d.X + d.Y*d.Y))) l := float32(math.Sqrt(float64(d.X*d.X + d.Y*d.Y)))
a := float32(math.Atan2(float64(-d.Y), float64(d.X))) a := float32(math.Atan2(float64(-d.Y), float64(d.X)))
// TODO: optimize // TODO: optimize
zp := f32.Point{} zp := f32.Point{}
return f32.AffineId(). return f32.Affine2D{}.
Scale(zp, layout.FPt(clip.Size())). // scale to pixel space Scale(zp, layout.FPt(clip.Size())). // scale to pixel space
Offset(zp.Sub(f32.FPt(off)).Add(layout.FPt(clip.Min))). // offset to clip space Offset(zp.Sub(off).Add(layout.FPt(clip.Min))). // offset to clip space
Offset(zp.Sub(stop1)). // offset to first stop point Offset(zp.Sub(stop1)). // offset to first stop point
Rotate(zp, a). // rotate to align gradient Rotate(zp, a). // rotate to align gradient
Scale(zp, f32.Pt(1/l, 1/l)) // scale gradient to right size Scale(zp, f32.Pt(1/l, 1/l)) // scale gradient to right size
} }
// clipSpaceTransform returns the scale and offset that transforms the given // clipSpaceTransform returns the scale and offset that transforms the given
@@ -1486,7 +1484,7 @@ func (d *drawOps) buildVerts(pathData []byte, tr f32.Affine2D, outline bool, str
// as needed and feeds them to the supplied splitter. // as needed and feeds them to the supplied splitter.
func decodeToOutlineQuads(qs *quadSplitter, tr f32.Affine2D, pathData []byte) { func decodeToOutlineQuads(qs *quadSplitter, tr f32.Affine2D, pathData []byte) {
for len(pathData) >= scene.CommandSize+4 { for len(pathData) >= scene.CommandSize+4 {
qs.contour = binary.LittleEndian.Uint32(pathData) qs.contour = bo.Uint32(pathData)
cmd := ops.DecodeCommand(pathData[4:]) cmd := ops.DecodeCommand(pathData[4:])
switch cmd.Op() { switch cmd.Op() {
case scene.OpLine: case scene.OpLine:
@@ -1521,38 +1519,37 @@ func decodeToOutlineQuads(qs *quadSplitter, tr f32.Affine2D, pathData []byte) {
} }
// create GPU vertices for transformed r, find the bounds and establish texture transform. // create GPU vertices for transformed r, find the bounds and establish texture transform.
func (d *drawOps) boundsForTransformedRect(r image.Rectangle, tr f32.Affine2D) (aux []byte, bnd image.Rectangle, ptr f32.Affine2D) { func (d *drawOps) boundsForTransformedRect(r f32.Rectangle, tr f32.Affine2D) (aux []byte, bnd f32.Rectangle, ptr f32.Affine2D) {
ptr = f32.AffineId() if isPureOffset(tr) {
if tr == f32.AffineId() { // fast-path to allow blitting of pure rectangles
// fast-path to allow blitting of pure rectangles. _, _, ox, _, _, oy := tr.Elems()
bnd = r off := f32.Pt(ox, oy)
bnd.Min = r.Min.Add(off)
bnd.Max = r.Max.Add(off)
return return
} }
// transform all corners, find new bounds // transform all corners, find new bounds
corners := [4]f32.Point{ corners := [4]f32.Point{
tr.Transform(f32.FPt(r.Min)), tr.Transform(f32.Pt(float32(r.Max.X), float32(r.Min.Y))), tr.Transform(r.Min), tr.Transform(f32.Pt(r.Max.X, r.Min.Y)),
tr.Transform(f32.FPt(r.Max)), tr.Transform(f32.Pt(float32(r.Min.X), float32(r.Max.Y))), tr.Transform(r.Max), tr.Transform(f32.Pt(r.Min.X, r.Max.Y)),
}
fBounds := f32.Rectangle{
Min: f32.Pt(math.MaxFloat32, math.MaxFloat32),
Max: f32.Pt(-math.MaxFloat32, -math.MaxFloat32),
} }
bnd.Min = f32.Pt(math.MaxFloat32, math.MaxFloat32)
bnd.Max = f32.Pt(-math.MaxFloat32, -math.MaxFloat32)
for _, c := range corners { for _, c := range corners {
if c.X < fBounds.Min.X { if c.X < bnd.Min.X {
fBounds.Min.X = c.X bnd.Min.X = c.X
} }
if c.Y < fBounds.Min.Y { if c.Y < bnd.Min.Y {
fBounds.Min.Y = c.Y bnd.Min.Y = c.Y
} }
if c.X > fBounds.Max.X { if c.X > bnd.Max.X {
fBounds.Max.X = c.X bnd.Max.X = c.X
} }
if c.Y > fBounds.Max.Y { if c.Y > bnd.Max.Y {
fBounds.Max.Y = c.Y bnd.Max.Y = c.Y
} }
} }
bnd = fBounds.Round()
// build the GPU vertices // build the GPU vertices
l := len(d.vertCache) l := len(d.vertCache)
@@ -1566,38 +1563,19 @@ func (d *drawOps) boundsForTransformedRect(r image.Rectangle, tr f32.Affine2D) (
// establish the transform mapping from bounds rectangle to transformed corners // establish the transform mapping from bounds rectangle to transformed corners
var P1, P2, P3 f32.Point var P1, P2, P3 f32.Point
P1.X = (corners[1].X - fBounds.Min.X) / (fBounds.Max.X - fBounds.Min.X) P1.X = (corners[1].X - bnd.Min.X) / (bnd.Max.X - bnd.Min.X)
P1.Y = (corners[1].Y - fBounds.Min.Y) / (fBounds.Max.Y - fBounds.Min.Y) P1.Y = (corners[1].Y - bnd.Min.Y) / (bnd.Max.Y - bnd.Min.Y)
P2.X = (corners[2].X - fBounds.Min.X) / (fBounds.Max.X - fBounds.Min.X) P2.X = (corners[2].X - bnd.Min.X) / (bnd.Max.X - bnd.Min.X)
P2.Y = (corners[2].Y - fBounds.Min.Y) / (fBounds.Max.Y - fBounds.Min.Y) P2.Y = (corners[2].Y - bnd.Min.Y) / (bnd.Max.Y - bnd.Min.Y)
P3.X = (corners[3].X - fBounds.Min.X) / (fBounds.Max.X - fBounds.Min.X) P3.X = (corners[3].X - bnd.Min.X) / (bnd.Max.X - bnd.Min.X)
P3.Y = (corners[3].Y - fBounds.Min.Y) / (fBounds.Max.Y - fBounds.Min.Y) P3.Y = (corners[3].Y - bnd.Min.Y) / (bnd.Max.Y - bnd.Min.Y)
sx, sy := P2.X-P3.X, P2.Y-P3.Y sx, sy := P2.X-P3.X, P2.Y-P3.Y
ptr = f32.NewAffine2D(sx, P2.X-P1.X, P1.X-sx, sy, P2.Y-P1.Y, P1.Y-sy).Invert() ptr = f32.NewAffine2D(sx, P2.X-P1.X, P1.X-sx, sy, P2.Y-P1.Y, P1.Y-sy).Invert()
return aux, bnd, ptr
}
// transformOffset a transform into two parts, one which is pure integer offset
// and the other representing the scaling, shearing and rotation and fractional
// offset.
func transformOffset(t f32.Affine2D) (f32.Affine2D, image.Point) {
sx, hx, ox, hy, sy, oy := t.Elems()
iox, fox := math.Modf(float64(ox))
ioy, foy := math.Modf(float64(oy))
ft := f32.NewAffine2D(sx, hx, float32(fox), hy, sy, float32(foy))
ip := image.Pt(int(iox), int(ioy))
return ft, ip
}
func newShaders(ctx driver.Device, vsrc, fsrc shader.Sources) (vert driver.VertexShader, frag driver.FragmentShader, err error) {
vert, err = ctx.NewVertexShader(vsrc)
if err != nil {
return
}
frag, err = ctx.NewFragmentShader(fsrc)
if err != nil {
vert.Release()
}
return return
} }
func isPureOffset(t f32.Affine2D) bool {
a, b, _, d, e, _ := t.Elems()
return a == 1 && b == 0 && d == 0 && e == 1
}
+3 -5
View File
@@ -21,10 +21,8 @@ import (
var dumpImages = flag.Bool("saveimages", false, "save test images") var dumpImages = flag.Bool("saveimages", false, "save test images")
var ( var clearCol = color.NRGBA{A: 0xff, R: 0xde, G: 0xad, B: 0xbe}
clearCol = color.NRGBA{A: 0xff, R: 0xde, G: 0xad, B: 0xbe} var clearColExpect = f32color.NRGBAToRGBA(clearCol)
clearColExpect = f32color.NRGBAToRGBA(clearCol)
)
func TestFramebufferClear(t *testing.T) { func TestFramebufferClear(t *testing.T) {
b := newDriver(t) b := newDriver(t)
@@ -204,5 +202,5 @@ func saveImage(file string, img image.Image) error {
if err := png.Encode(&buf, img); err != nil { if err := png.Encode(&buf, img); err != nil {
return err return err
} }
return os.WriteFile(file, buf.Bytes(), 0o666) return os.WriteFile(file, buf.Bytes(), 0666)
} }
+8 -3
View File
@@ -152,7 +152,9 @@ func newDirect3D11Device(api driver.Direct3D11) (driver.Device, error) {
} }
func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Texture { func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Texture {
var renderTarget *d3d11.RenderTargetView var (
renderTarget *d3d11.RenderTargetView
)
if target != nil { if target != nil {
switch t := target.(type) { switch t := target.(type) {
case driver.Direct3D11RenderTarget: case driver.Direct3D11RenderTarget:
@@ -227,7 +229,10 @@ func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, min
// Flags required by ID3D11DeviceContext::GenerateMips. // Flags required by ID3D11DeviceContext::GenerateMips.
bindFlags |= d3d11.BIND_SHADER_RESOURCE | d3d11.BIND_RENDER_TARGET bindFlags |= d3d11.BIND_SHADER_RESOURCE | d3d11.BIND_RENDER_TARGET
miscFlags |= d3d11.RESOURCE_MISC_GENERATE_MIPS miscFlags |= d3d11.RESOURCE_MISC_GENERATE_MIPS
dim := max(height, width) dim := width
if height > dim {
dim = height
}
log2 := 32 - bits.LeadingZeros32(uint32(dim)) - 1 log2 := 32 - bits.LeadingZeros32(uint32(dim)) - 1
nmipmaps = log2 + 1 nmipmaps = log2 + 1
} }
@@ -797,7 +802,7 @@ func (t *Texture) ReadPixels(src image.Rectangle, pixels []byte, stride int) err
mapSize := dstPitch * h mapSize := dstPitch * h
data := sliceOf(resMap.PData, mapSize) data := sliceOf(resMap.PData, mapSize)
width := w * 4 width := w * 4
for r := range h { for r := 0; r < h; r++ {
pixels := pixels[r*srcPitch:] pixels := pixels[r*srcPitch:]
copy(pixels[:width], data[r*dstPitch:]) copy(pixels[:width], data[r*dstPitch:])
} }
+3 -5
View File
@@ -96,10 +96,8 @@ type BlendFactor uint8
type Topology uint8 type Topology uint8
type ( type TextureFilter uint8
TextureFilter uint8 type TextureFormat uint8
TextureFormat uint8
)
type BufferBinding uint8 type BufferBinding uint8
@@ -219,7 +217,7 @@ func flipImageY(stride, height int, pixels []byte) {
// Flip image in y-direction. OpenGL's origin is in the lower // Flip image in y-direction. OpenGL's origin is in the lower
// left corner. // left corner.
row := make([]uint8, stride) row := make([]uint8, stride)
for y := range height / 2 { for y := 0; y < height/2; y++ {
y1 := height - y - 1 y1 := height - y - 1
dest := y1 * stride dest := y1 * stride
src := y * stride src := y * stride
+12 -5
View File
@@ -8,7 +8,6 @@ import (
"image" "image"
"math/bits" "math/bits"
"runtime" "runtime"
"slices"
"strings" "strings"
"time" "time"
"unsafe" "unsafe"
@@ -718,7 +717,10 @@ func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, min
if mipmap { if mipmap {
nmipmaps := 1 nmipmaps := 1
if mipmap { if mipmap {
dim := max(height, width) dim := width
if height > dim {
dim = height
}
log2 := 32 - bits.LeadingZeros32(uint32(dim)) - 1 log2 := 32 - bits.LeadingZeros32(uint32(dim)) - 1
nmipmaps = log2 + 1 nmipmaps = log2 + 1
} }
@@ -1130,7 +1132,7 @@ func (b *Backend) setupVertexArrays() {
enabled[inp.Location] = true enabled[inp.Location] = true
b.glstate.vertexAttribPointer(b.funcs, buf.obj, inp.Location, l.Size, gltyp, false, p.layout.Stride, buf.offset+l.Offset) b.glstate.vertexAttribPointer(b.funcs, buf.obj, inp.Location, l.Size, gltyp, false, p.layout.Stride, buf.offset+l.Offset)
} }
for i := range max { for i := 0; i < max; i++ {
b.glstate.setVertexAttribArray(b.funcs, i, enabled[i]) b.glstate.setVertexAttribArray(b.funcs, i, enabled[i])
} }
} }
@@ -1173,7 +1175,7 @@ func (t *texture) ReadPixels(src image.Rectangle, pixels []byte, stride int) err
} else { } else {
tmp := make([]byte, w*h*4) tmp := make([]byte, w*h*4)
t.backend.funcs.ReadPixels(src.Min.X, src.Min.Y, w, h, gl.RGBA, gl.UNSIGNED_BYTE, tmp) t.backend.funcs.ReadPixels(src.Min.X, src.Min.Y, w, h, gl.RGBA, gl.UNSIGNED_BYTE, tmp)
for y := range h { for y := 0; y < h; y++ {
copy(pixels[y*stride:], tmp[y*w*4:]) copy(pixels[y*stride:], tmp[y*w*4:])
} }
} }
@@ -1355,7 +1357,12 @@ func alphaTripleFor(ver [2]int) textureTriple {
} }
func hasExtension(exts []string, ext string) bool { func hasExtension(exts []string, ext string) bool {
return slices.Contains(exts, ext) for _, e := range exts {
if ext == e {
return true
}
}
return false
} }
func firstBufferType(typ driver.BufferBinding) gl.Enum { func firstBufferType(typ driver.BufferBinding) gl.Enum {
+21 -21
View File
@@ -66,8 +66,8 @@ func BenchmarkDrawUICached(b *testing.B) {
defer w.Release() defer w.Release()
drawCore(gtx, th) drawCore(gtx, th)
w.Frame(gtx.Ops) w.Frame(gtx.Ops)
b.ResetTimer()
for b.Loop() { for i := 0; i < b.N; i++ {
w.Frame(gtx.Ops) w.Frame(gtx.Ops)
} }
finishBenchmark(b, w) finishBenchmark(b, w)
@@ -83,12 +83,12 @@ func BenchmarkDrawUI(b *testing.B) {
drawCore(gtx, th) drawCore(gtx, th)
w.Frame(gtx.Ops) w.Frame(gtx.Ops)
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer()
for i := 0; b.Loop(); i++ { for i := 0; i < b.N; i++ {
resetOps(gtx) resetOps(gtx)
off := float32(math.Mod(float64(i)/10, 10)) off := float32(math.Mod(float64(i)/10, 10))
t := op.Affine(f32.AffineId().Offset(f32.Pt(off, off))).Push(gtx.Ops) t := op.Affine(f32.Affine2D{}.Offset(f32.Pt(off, off))).Push(gtx.Ops)
drawCore(gtx, th) drawCore(gtx, th)
@@ -105,12 +105,12 @@ func BenchmarkDrawUITransformed(b *testing.B) {
drawCore(gtx, th) drawCore(gtx, th)
w.Frame(gtx.Ops) w.Frame(gtx.Ops)
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer()
for i := 0; b.Loop(); i++ { for i := 0; i < b.N; i++ {
resetOps(gtx) resetOps(gtx)
angle := float32(math.Mod(float64(i)/1000, 0.05)) angle := float32(math.Mod(float64(i)/1000, 0.05))
a := f32.AffineId().Shear(f32.Point{}, angle, angle).Rotate(f32.Point{}, angle) a := f32.Affine2D{}.Shear(f32.Point{}, angle, angle).Rotate(f32.Point{}, angle)
t := op.Affine(a).Push(gtx.Ops) t := op.Affine(a).Push(gtx.Ops)
drawCore(gtx, th) drawCore(gtx, th)
@@ -130,8 +130,8 @@ func Benchmark1000Circles(b *testing.B) {
draw1000Circles(gtx) draw1000Circles(gtx)
w.Frame(gtx.Ops) w.Frame(gtx.Ops)
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer()
for b.Loop() { for i := 0; i < b.N; i++ {
resetOps(gtx) resetOps(gtx)
draw1000Circles(gtx) draw1000Circles(gtx)
w.Frame(gtx.Ops) w.Frame(gtx.Ops)
@@ -147,8 +147,8 @@ func Benchmark1000CirclesInstanced(b *testing.B) {
draw1000CirclesInstanced(gtx) draw1000CirclesInstanced(gtx)
w.Frame(gtx.Ops) w.Frame(gtx.Ops)
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer()
for b.Loop() { for i := 0; i < b.N; i++ {
resetOps(gtx) resetOps(gtx)
draw1000CirclesInstanced(gtx) draw1000CirclesInstanced(gtx)
w.Frame(gtx.Ops) w.Frame(gtx.Ops)
@@ -158,9 +158,9 @@ func Benchmark1000CirclesInstanced(b *testing.B) {
func draw1000Circles(gtx layout.Context) { func draw1000Circles(gtx layout.Context) {
ops := gtx.Ops ops := gtx.Ops
for x := range 100 { for x := 0; x < 100; x++ {
op.Offset(image.Pt(x*10, 0)).Add(ops) op.Offset(image.Pt(x*10, 0)).Add(ops)
for y := range 10 { for y := 0; y < 10; y++ {
paint.FillShape(ops, paint.FillShape(ops,
color.NRGBA{R: 100 + uint8(x), G: 100 + uint8(y), B: 100, A: 120}, color.NRGBA{R: 100 + uint8(x), G: 100 + uint8(y), B: 100, A: 120},
clip.RRect{Rect: image.Rect(0, 0, 10, 10), NE: 5, SE: 5, SW: 5, NW: 5}.Op(ops), clip.RRect{Rect: image.Rect(0, 0, 10, 10), NE: 5, SE: 5, SW: 5, NW: 5}.Op(ops),
@@ -179,9 +179,9 @@ func draw1000CirclesInstanced(gtx layout.Context) {
cl.Pop() cl.Pop()
c := r.Stop() c := r.Stop()
for x := range 100 { for x := 0; x < 100; x++ {
op.Offset(image.Pt(x*10, 0)).Add(ops) op.Offset(image.Pt(x*10, 0)).Add(ops)
for y := range 10 { for y := 0; y < 10; y++ {
paint.ColorOp{Color: color.NRGBA{R: 100 + uint8(x), G: 100 + uint8(y), B: 100, A: 120}}.Add(ops) paint.ColorOp{Color: color.NRGBA{R: 100 + uint8(x), G: 100 + uint8(y), B: 100, A: 120}}.Add(ops)
c.Add(ops) c.Add(ops)
op.Offset(image.Pt(0, 100)).Add(ops) op.Offset(image.Pt(0, 100)).Add(ops)
@@ -204,9 +204,9 @@ func drawIndividualShapes(gtx layout.Context, th *material.Theme) chan op.CallOp
go func() { go func() {
ops := &op1 ops := &op1
c := op.Record(ops) c := op.Record(ops)
for x := range 9 { for x := 0; x < 9; x++ {
op.Offset(image.Pt(x*50, 0)).Add(ops) op.Offset(image.Pt(x*50, 0)).Add(ops)
for y := range 9 { for y := 0; y < 9; y++ {
paint.FillShape(ops, paint.FillShape(ops,
color.NRGBA{R: 100 + uint8(x), G: 100 + uint8(y), B: 100, A: 120}, color.NRGBA{R: 100 + uint8(x), G: 100 + uint8(y), B: 100, A: 120},
clip.RRect{Rect: image.Rect(0, 0, 25, 25), NE: 10, SE: 10, SW: 10, NW: 10}.Op(ops), clip.RRect{Rect: image.Rect(0, 0, 25, 25), NE: 10, SE: 10, SW: 10, NW: 10}.Op(ops),
@@ -233,8 +233,8 @@ func drawShapeInstances(gtx layout.Context, th *material.Theme) chan op.CallOp {
squares.Add(ops) squares.Add(ops)
rad := float32(0) rad := float32(0)
for x := range 20 { for x := 0; x < 20; x++ {
for y := range 20 { for y := 0; y < 20; y++ {
t := op.Offset(image.Pt(x*50+25, y*50+25)).Push(ops) t := op.Offset(image.Pt(x*50+25, y*50+25)).Push(ops)
c.Add(ops) c.Add(ops)
t.Pop() t.Pop()
@@ -253,7 +253,7 @@ func drawText(gtx layout.Context, th *material.Theme) chan op.CallOp {
c := op.Record(ops) c := op.Record(ops)
txt := material.H6(th, "") txt := material.H6(th, "")
for x := range 40 { for x := 0; x < 40; x++ {
txt.Text = textRows[x] txt.Text = textRows[x]
t := op.Offset(image.Pt(0, 24*x)).Push(ops) t := op.Offset(image.Pt(0, 24*x)).Push(ops)
gtx.Ops = ops gtx.Ops = ops
+9 -40
View File
@@ -40,25 +40,6 @@ func TestPaintClippedRect(t *testing.T) {
}) })
} }
func TestPaintClippedRectOffset(t *testing.T) {
run(t, func(o *op.Ops) {
defer op.Affine(f32.AffineId().Offset(f32.Pt(0.5, 0.5))).Push(o).Pop()
defer clip.RRect{Rect: image.Rect(25, 25, 60, 60)}.Push(o).Pop()
paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 50, 50)).Op())
}, func(r result) {
r.expect(0, 0, transparent)
r.expect(24, 35, transparent)
r.expect(24, 24, transparent)
r.expect(25, 25, color.RGBA{R: 137, A: 64})
r.expect(25, 35, color.RGBA{R: 187, A: 128})
r.expect(35, 25, color.RGBA{R: 187, A: 128})
r.expect(50, 50, color.RGBA{R: 137, A: 64})
r.expect(51, 51, transparent)
r.expect(50, 0, transparent)
r.expect(10, 50, transparent)
})
}
func TestPaintClippedCircle(t *testing.T) { func TestPaintClippedCircle(t *testing.T) {
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
const r = 10 const r = 10
@@ -151,7 +132,8 @@ func TestTexturedStrokeClipped(t *testing.T) {
defer clip.RRect{Rect: image.Rect(-30, -30, 60, 60)}.Push(o).Pop() defer clip.RRect{Rect: image.Rect(-30, -30, 60, 60)}.Push(o).Pop()
defer op.Offset(image.Pt(-10, -10)).Push(o).Pop() defer op.Offset(image.Pt(-10, -10)).Push(o).Pop()
paint.PaintOp{}.Add(o) paint.PaintOp{}.Add(o)
}, nil) }, func(r result) {
})
} }
func TestTexturedStroke(t *testing.T) { func TestTexturedStroke(t *testing.T) {
@@ -164,7 +146,8 @@ func TestTexturedStroke(t *testing.T) {
}.Op().Push(o).Pop() }.Op().Push(o).Pop()
defer op.Offset(image.Pt(-10, -10)).Push(o).Pop() defer op.Offset(image.Pt(-10, -10)).Push(o).Pop()
paint.PaintOp{}.Add(o) paint.PaintOp{}.Add(o)
}, nil) }, func(r result) {
})
} }
func TestPaintClippedTexture(t *testing.T) { func TestPaintClippedTexture(t *testing.T) {
@@ -195,6 +178,7 @@ func TestStrokedPathZeroWidth(t *testing.T) {
paint.Fill(o, red) paint.Fill(o, red)
cl.Pop() cl.Pop()
} }
}, func(r result) { }, func(r result) {
r.expect(0, 0, transparent) r.expect(0, 0, transparent)
r.expect(10, 50, colornames.Black) r.expect(10, 50, colornames.Black)
@@ -268,7 +252,8 @@ func TestPathReuse(t *testing.T) {
stroke := clip.Stroke{Path: spec, Width: 3}.Op().Push(o) stroke := clip.Stroke{Path: spec, Width: 3}.Op().Push(o)
paint.Fill(o, color.NRGBA{B: 0xFF, A: 0xFF}) paint.Fill(o, color.NRGBA{B: 0xFF, A: 0xFF})
stroke.Pop() stroke.Pop()
}, nil) }, func(r result) {
})
} }
func TestPathInterleave(t *testing.T) { func TestPathInterleave(t *testing.T) {
@@ -305,22 +290,6 @@ func TestStrokedRect(t *testing.T) {
Width: 5, Width: 5,
}.Op(), }.Op(),
) )
}, nil) }, func(r result) {
} })
func TestInstancedRects(t *testing.T) {
run(t, func(o *op.Ops) {
macro := op.Record(o)
clip := clip.Rect{Max: image.Pt(20, 20)}.Push(o)
paint.ColorOp{Color: color.NRGBA{R: 0x80, A: 0xFF}}.Add(o)
paint.PaintOp{}.Add(o)
clip.Pop()
c := macro.Stop()
for range 2 {
op.Affine(f32.AffineId().Rotate(f32.Pt(0, 0), .2)).Add(o)
c.Add(o)
op.Offset(image.Pt(20, 20)).Add(o)
}
}, nil)
} }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 787 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 435 B

+15 -14
View File
@@ -24,6 +24,7 @@ func TestTransformMacro(t *testing.T) {
c := constSqPath() c := constSqPath()
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
// render the first Stacked item // render the first Stacked item
m1 := op.Record(o) m1 := op.Record(o)
dr := image.Rect(0, 0, 128, 50) dr := image.Rect(0, 0, 128, 50)
@@ -87,10 +88,10 @@ func TestNoClipFromPaint(t *testing.T) {
// ensure that a paint operation does not pollute the state // ensure that a paint operation does not pollute the state
// by leaving any clip paths in place. // by leaving any clip paths in place.
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
a := f32.AffineId().Rotate(f32.Pt(20, 20), math.Pi/4) a := f32.Affine2D{}.Rotate(f32.Pt(20, 20), math.Pi/4)
defer op.Affine(a).Push(o).Pop() defer op.Affine(a).Push(o).Pop()
paint.FillShape(o, red, clip.Rect(image.Rect(10, 10, 30, 30)).Op()) paint.FillShape(o, red, clip.Rect(image.Rect(10, 10, 30, 30)).Op())
a = f32.AffineId().Rotate(f32.Pt(20, 20), -math.Pi/4) a = f32.Affine2D{}.Rotate(f32.Pt(20, 20), -math.Pi/4)
defer op.Affine(a).Push(o).Pop() defer op.Affine(a).Push(o).Pop()
paint.FillShape(o, black, clip.Rect(image.Rect(0, 0, 50, 50)).Op()) paint.FillShape(o, black, clip.Rect(image.Rect(0, 0, 50, 50)).Op())
@@ -109,7 +110,7 @@ func TestDeferredPaint(t *testing.T) {
paint.PaintOp{}.Add(o) paint.PaintOp{}.Add(o)
cl.Pop() cl.Pop()
t := op.Affine(f32.AffineId().Offset(f32.Pt(20, 20))).Push(o) t := op.Affine(f32.Affine2D{}.Offset(f32.Pt(20, 20))).Push(o)
m := op.Record(o) m := op.Record(o)
cl2 := clip.Rect(image.Rect(0, 0, 80, 80)).Op().Push(o) cl2 := clip.Rect(image.Rect(0, 0, 80, 80)).Op().Push(o)
paint.ColorOp{Color: color.NRGBA{A: 0x60, R: 0xff, G: 0xff}}.Add(o) paint.ColorOp{Color: color.NRGBA{A: 0x60, R: 0xff, G: 0xff}}.Add(o)
@@ -119,11 +120,12 @@ func TestDeferredPaint(t *testing.T) {
op.Defer(o, paintMacro) op.Defer(o, paintMacro)
t.Pop() t.Pop()
defer op.Affine(f32.AffineId().Offset(f32.Pt(10, 10))).Push(o).Pop() defer op.Affine(f32.Affine2D{}.Offset(f32.Pt(10, 10))).Push(o).Pop()
defer clip.Rect(image.Rect(0, 0, 80, 80)).Op().Push(o).Pop() defer clip.Rect(image.Rect(0, 0, 80, 80)).Op().Push(o).Pop()
paint.ColorOp{Color: color.NRGBA{A: 0x60, B: 0xff}}.Add(o) paint.ColorOp{Color: color.NRGBA{A: 0x60, B: 0xff}}.Add(o)
paint.PaintOp{}.Add(o) paint.PaintOp{}.Add(o)
}, nil) }, func(r result) {
})
} }
func constSqPath() clip.Op { func constSqPath() clip.Op {
@@ -141,10 +143,8 @@ func constSqPath() clip.Op {
func constSqCirc() clip.Op { func constSqCirc() clip.Op {
innerOps := new(op.Ops) innerOps := new(op.Ops)
return clip.RRect{ return clip.RRect{Rect: image.Rect(0, 0, 40, 40),
Rect: image.Rect(0, 0, 40, 40), NW: 20, NE: 20, SW: 20, SE: 20}.Op(innerOps)
NW: 20, NE: 20, SW: 20, SE: 20,
}.Op(innerOps)
} }
func drawChild(ops *op.Ops, text clip.Op) op.CallOp { func drawChild(ops *op.Ops, text clip.Op) op.CallOp {
@@ -260,7 +260,7 @@ func TestLinearGradient(t *testing.T) {
Color2: g.To, Color2: g.To,
}.Add(ops) }.Add(ops)
cl := clip.RRect{Rect: gr.Round()}.Push(ops) cl := clip.RRect{Rect: gr.Round()}.Push(ops)
t1 := op.Affine(f32.AffineId().Offset(pixelAligned.Min)).Push(ops) t1 := op.Affine(f32.Affine2D{}.Offset(pixelAligned.Min)).Push(ops)
t2 := scale(pixelAligned.Dx()/128, 1).Push(ops) t2 := scale(pixelAligned.Dx()/128, 1).Push(ops)
paint.PaintOp{}.Add(ops) paint.PaintOp{}.Add(ops)
t2.Pop() t2.Pop()
@@ -323,7 +323,7 @@ func TestLinearGradientAngled(t *testing.T) {
cl = clip.Rect(image.Rect(0, 64, 64, 128)).Push(ops) cl = clip.Rect(image.Rect(0, 64, 64, 128)).Push(ops)
paint.PaintOp{}.Add(ops) paint.PaintOp{}.Add(ops)
cl.Pop() cl.Pop()
}, nil) }, func(r result) {})
} }
func TestZeroImage(t *testing.T) { func TestZeroImage(t *testing.T) {
@@ -363,7 +363,7 @@ func TestImageRGBA_ScaleLinear(t *testing.T) {
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
w := newWindow(t, 128, 128) w := newWindow(t, 128, 128)
defer clip.Rect{Max: image.Pt(128, 128)}.Push(o).Pop() defer clip.Rect{Max: image.Pt(128, 128)}.Push(o).Pop()
op.Affine(f32.AffineId().Scale(f32.Point{}, f32.Pt(64, 64))).Add(o) op.Affine(f32.Affine2D{}.Scale(f32.Point{}, f32.Pt(64, 64))).Add(o)
im := image.NewRGBA(image.Rect(0, 0, 2, 2)) im := image.NewRGBA(image.Rect(0, 0, 2, 2))
im.Set(0, 0, colornames.Red) im.Set(0, 0, colornames.Red)
@@ -397,7 +397,7 @@ func TestImageRGBA_ScaleLinear(t *testing.T) {
func TestImageRGBA_ScaleNearest(t *testing.T) { func TestImageRGBA_ScaleNearest(t *testing.T) {
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
w := newWindow(t, 128, 128) w := newWindow(t, 128, 128)
op.Affine(f32.AffineId().Scale(f32.Point{}, f32.Pt(64, 64))).Add(o) op.Affine(f32.Affine2D{}.Scale(f32.Point{}, f32.Pt(64, 64))).Add(o)
im := image.NewRGBA(image.Rect(0, 0, 2, 2)) im := image.NewRGBA(image.Rect(0, 0, 2, 2))
im.Set(0, 0, colornames.Red) im.Set(0, 0, colornames.Red)
@@ -492,7 +492,8 @@ func TestOpacity(t *testing.T) {
opc3 := paint.PushOpacity(ops, .6) opc3 := paint.PushOpacity(ops, .6)
paint.FillShape(ops, color.NRGBA{B: 255, A: 255}, clip.Ellipse(image.Rectangle{Min: image.Pt(20+20, 10), Max: image.Pt(50+64, 128)}).Op(ops)) paint.FillShape(ops, color.NRGBA{B: 255, A: 255}, clip.Ellipse(image.Rectangle{Min: image.Pt(20+20, 10), Max: image.Pt(50+64, 128)}).Op(ops))
opc3.Pop() opc3.Pop()
}, nil) }, func(r result) {
})
} }
// lerp calculates linear interpolation with color b and p. // lerp calculates linear interpolation with color b and p.
+12 -12
View File
@@ -29,7 +29,7 @@ func TestPaintOffset(t *testing.T) {
func TestPaintRotate(t *testing.T) { func TestPaintRotate(t *testing.T) {
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
a := f32.AffineId().Rotate(f32.Pt(40, 40), -math.Pi/8) a := f32.Affine2D{}.Rotate(f32.Pt(40, 40), -math.Pi/8)
defer op.Affine(a).Push(o).Pop() defer op.Affine(a).Push(o).Pop()
paint.FillShape(o, red, clip.Rect(image.Rect(20, 20, 60, 60)).Op()) paint.FillShape(o, red, clip.Rect(image.Rect(20, 20, 60, 60)).Op())
}, func(r result) { }, func(r result) {
@@ -42,7 +42,7 @@ func TestPaintRotate(t *testing.T) {
func TestPaintShear(t *testing.T) { func TestPaintShear(t *testing.T) {
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
a := f32.AffineId().Shear(f32.Point{}, math.Pi/4, 0) a := f32.Affine2D{}.Shear(f32.Point{}, math.Pi/4, 0)
defer op.Affine(a).Push(o).Pop() defer op.Affine(a).Push(o).Pop()
paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 40, 40)).Op()) paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 40, 40)).Op())
}, func(r result) { }, func(r result) {
@@ -79,7 +79,7 @@ func TestClipOffset(t *testing.T) {
func TestClipScale(t *testing.T) { func TestClipScale(t *testing.T) {
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
a := f32.AffineId().Scale(f32.Point{}, f32.Pt(2, 2)).Offset(f32.Pt(10, 10)) a := f32.Affine2D{}.Scale(f32.Point{}, f32.Pt(2, 2)).Offset(f32.Pt(10, 10))
defer op.Affine(a).Push(o).Pop() defer op.Affine(a).Push(o).Pop()
defer clip.RRect{Rect: image.Rect(10, 10, 20, 20)}.Push(o).Pop() defer clip.RRect{Rect: image.Rect(10, 10, 20, 20)}.Push(o).Pop()
paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 1000, 1000)).Op()) paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 1000, 1000)).Op())
@@ -93,7 +93,7 @@ func TestClipScale(t *testing.T) {
func TestClipRotate(t *testing.T) { func TestClipRotate(t *testing.T) {
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
defer op.Affine(f32.AffineId().Rotate(f32.Pt(40, 40), -math.Pi/4)).Push(o).Pop() defer op.Affine(f32.Affine2D{}.Rotate(f32.Pt(40, 40), -math.Pi/4)).Push(o).Pop()
defer clip.RRect{Rect: image.Rect(30, 30, 50, 50)}.Push(o).Pop() defer clip.RRect{Rect: image.Rect(30, 30, 50, 50)}.Push(o).Pop()
paint.FillShape(o, red, clip.Rect(image.Rect(0, 40, 100, 100)).Op()) paint.FillShape(o, red, clip.Rect(image.Rect(0, 40, 100, 100)).Op())
}, func(r result) { }, func(r result) {
@@ -121,7 +121,7 @@ func TestOffsetScaleTexture(t *testing.T) {
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
defer op.Offset(image.Pt(15, 15)).Push(o).Pop() defer op.Offset(image.Pt(15, 15)).Push(o).Pop()
squares.Add(o) squares.Add(o)
defer op.Affine(f32.AffineId().Scale(f32.Point{}, f32.Pt(2, 1))).Push(o).Pop() defer op.Affine(f32.Affine2D{}.Scale(f32.Point{}, f32.Pt(2, 1))).Push(o).Pop()
defer scale(50.0/512, 50.0/512).Push(o).Pop() defer scale(50.0/512, 50.0/512).Push(o).Pop()
paint.PaintOp{}.Add(o) paint.PaintOp{}.Add(o)
}, func(r result) { }, func(r result) {
@@ -133,7 +133,7 @@ func TestOffsetScaleTexture(t *testing.T) {
func TestRotateTexture(t *testing.T) { func TestRotateTexture(t *testing.T) {
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
squares.Add(o) squares.Add(o)
a := f32.AffineId().Offset(f32.Pt(30, 30)).Rotate(f32.Pt(40, 40), math.Pi/4) a := f32.Affine2D{}.Offset(f32.Pt(30, 30)).Rotate(f32.Pt(40, 40), math.Pi/4)
defer op.Affine(a).Push(o).Pop() defer op.Affine(a).Push(o).Pop()
defer scale(20.0/512, 20.0/512).Push(o).Pop() defer scale(20.0/512, 20.0/512).Push(o).Pop()
paint.PaintOp{}.Add(o) paint.PaintOp{}.Add(o)
@@ -146,10 +146,10 @@ func TestRotateTexture(t *testing.T) {
func TestRotateClipTexture(t *testing.T) { func TestRotateClipTexture(t *testing.T) {
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
squares.Add(o) squares.Add(o)
a := f32.AffineId().Rotate(f32.Pt(40, 40), math.Pi/8) a := f32.Affine2D{}.Rotate(f32.Pt(40, 40), math.Pi/8)
defer op.Affine(a).Push(o).Pop() defer op.Affine(a).Push(o).Pop()
defer clip.RRect{Rect: image.Rect(30, 30, 50, 50)}.Push(o).Pop() defer clip.RRect{Rect: image.Rect(30, 30, 50, 50)}.Push(o).Pop()
defer op.Affine(f32.AffineId().Offset(f32.Pt(10, 10))).Push(o).Pop() defer op.Affine(f32.Affine2D{}.Offset(f32.Pt(10, 10))).Push(o).Pop()
defer scale(60.0/512, 60.0/512).Push(o).Pop() defer scale(60.0/512, 60.0/512).Push(o).Pop()
paint.PaintOp{}.Add(o) paint.PaintOp{}.Add(o)
}, func(r result) { }, func(r result) {
@@ -168,7 +168,7 @@ func TestComplicatedTransform(t *testing.T) {
defer clip.RRect{Rect: image.Rect(0, 0, 100, 100), SE: 50, SW: 50, NW: 50, NE: 50}.Push(o).Pop() defer clip.RRect{Rect: image.Rect(0, 0, 100, 100), SE: 50, SW: 50, NW: 50, NE: 50}.Push(o).Pop()
a := f32.AffineId().Shear(f32.Point{}, math.Pi/4, 0) a := f32.Affine2D{}.Shear(f32.Point{}, math.Pi/4, 0)
defer op.Affine(a).Push(o).Pop() defer op.Affine(a).Push(o).Pop()
defer clip.RRect{Rect: image.Rect(0, 0, 50, 40)}.Push(o).Pop() defer clip.RRect{Rect: image.Rect(0, 0, 50, 40)}.Push(o).Pop()
@@ -182,13 +182,13 @@ func TestComplicatedTransform(t *testing.T) {
func TestTransformOrder(t *testing.T) { func TestTransformOrder(t *testing.T) {
// check the ordering of operations bot in affine and in gpu stack. // check the ordering of operations bot in affine and in gpu stack.
run(t, func(o *op.Ops) { run(t, func(o *op.Ops) {
a := f32.AffineId().Offset(f32.Pt(64, 64)) a := f32.Affine2D{}.Offset(f32.Pt(64, 64))
defer op.Affine(a).Push(o).Pop() defer op.Affine(a).Push(o).Pop()
b := f32.AffineId().Scale(f32.Point{}, f32.Pt(8, 8)) b := f32.Affine2D{}.Scale(f32.Point{}, f32.Pt(8, 8))
defer op.Affine(b).Push(o).Pop() defer op.Affine(b).Push(o).Pop()
c := f32.AffineId().Offset(f32.Pt(-10, -10)).Scale(f32.Point{}, f32.Pt(0.5, 0.5)) c := f32.Affine2D{}.Offset(f32.Pt(-10, -10)).Scale(f32.Point{}, f32.Pt(0.5, 0.5))
defer op.Affine(c).Push(o).Pop() defer op.Affine(c).Push(o).Pop()
paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 20, 20)).Op()) paint.FillShape(o, red, clip.Rect(image.Rect(0, 0, 20, 20)).Op())
}, func(r result) { }, func(r result) {
+7 -9
View File
@@ -49,8 +49,8 @@ func buildSquares(size int) paint.ImageOp {
sub := size / 4 sub := size / 4
im := image.NewNRGBA(image.Rect(0, 0, size, size)) im := image.NewNRGBA(image.Rect(0, 0, size, size))
c1, c2 := image.NewUniform(colornames.Green), image.NewUniform(colornames.Blue) c1, c2 := image.NewUniform(colornames.Green), image.NewUniform(colornames.Blue)
for r := range 4 { for r := 0; r < 4; r++ {
for c := range 4 { for c := 0; c < 4; c++ {
c1, c2 = c2, c1 c1, c2 = c2, c1
draw.Draw(im, image.Rect(r*sub, c*sub, r*sub+sub, c*sub+sub), c1, image.Point{}, draw.Over) draw.Draw(im, image.Rect(r*sub, c*sub, r*sub+sub, c*sub+sub), c1, image.Point{}, draw.Over)
} }
@@ -78,7 +78,7 @@ func run(t *testing.T, f func(o *op.Ops), c func(r result)) {
var img *image.RGBA var img *image.RGBA
var err error var err error
ops := new(op.Ops) ops := new(op.Ops)
for i := range 3 { for i := 0; i < 3; i++ {
ops.Reset() ops.Reset()
img, err = drawImage(t, 128, ops, f) img, err = drawImage(t, 128, ops, f)
if err != nil { if err != nil {
@@ -90,9 +90,7 @@ func run(t *testing.T, f func(o *op.Ops), c func(r result)) {
name := fmt.Sprintf("%s-%d-bad.png", t.Name(), i) name := fmt.Sprintf("%s-%d-bad.png", t.Name(), i)
saveImage(t, name, img) saveImage(t, name, img)
} }
if c != nil { c(result{t: t, img: img})
c(result{t: t, img: img})
}
} }
} }
@@ -153,7 +151,7 @@ func verifyRef(t *testing.T, img *image.RGBA, frame int) (ok bool) {
} }
path = filepath.Join("refs", path+".png") path = filepath.Join("refs", path+".png")
if *dumpImages { if *dumpImages {
if err := os.MkdirAll(filepath.Dir(path), 0o766); err != nil { if err := os.MkdirAll(filepath.Dir(path), 0766); err != nil {
if !os.IsExist(err) { if !os.IsExist(err) {
t.Error(err) t.Error(err)
return return
@@ -287,7 +285,7 @@ func saveImage(t testing.TB, file string, img *image.RGBA) {
t.Error(err) t.Error(err)
return return
} }
if err := os.WriteFile(file, buf.Bytes(), 0o666); err != nil { if err := os.WriteFile(file, buf.Bytes(), 0666); err != nil {
t.Error(err) t.Error(err)
return return
} }
@@ -302,5 +300,5 @@ func newWindow(t testing.TB, width, height int) *headless.Window {
} }
func scale(sx, sy float32) op.TransformOp { func scale(sx, sy float32) op.TransformOp {
return op.Affine(f32.AffineId().Scale(f32.Point{}, f32.Pt(sx, sy))) return op.Affine(f32.Affine2D{}.Scale(f32.Point{}, f32.Pt(sx, sy)))
} }
+2 -2
View File
@@ -862,8 +862,8 @@ func (b *Backend) BindUniforms(buffer driver.Buffer) {
buf := buffer.(*Buffer) buf := buffer.(*Buffer)
cmdBuf := b.currentCmdBuf() cmdBuf := b.currentCmdBuf()
for _, s := range b.pipe.pushRanges { for _, s := range b.pipe.pushRanges {
off := vk.PushConstantRangeOffset(s) off := s.Offset()
vk.CmdPushConstants(cmdBuf, b.pipe.desc.layout, vk.PushConstantRangeStageFlags(s), off, buf.store[off:off+vk.PushConstantRangeSize(s)]) vk.CmdPushConstants(cmdBuf, b.pipe.desc.layout, s.StageFlags(), off, buf.store[off:off+s.Size()])
} }
} }
+2 -2
View File
@@ -10,10 +10,10 @@ import (
func BenchmarkPacker(b *testing.B) { func BenchmarkPacker(b *testing.B) {
var p packer var p packer
p.maxDims = image.Point{X: 4096, Y: 4096} p.maxDims = image.Point{X: 4096, Y: 4096}
for i := 0; b.Loop(); i++ { for i := 0; i < b.N; i++ {
p.clear() p.clear()
p.newPage() p.newPage()
for k := range 500 { for k := 0; k < 500; k++ {
_, ok := p.tryAdd(xy(k)) _, ok := p.tryAdd(xy(k))
if !ok { if !ok {
b.Fatal("add failed", i, k, xy(k)) b.Fatal("add failed", i, k, xy(k))
+5 -5
View File
@@ -150,7 +150,7 @@ func newCoverer(ctx driver.Device) *coverer {
c.texUniforms = new(coverTexUniforms) c.texUniforms = new(coverTexUniforms)
c.linearGradientUniforms = new(coverLinearGradientUniforms) c.linearGradientUniforms = new(coverLinearGradientUniforms)
pipelines, err := createColorPrograms(ctx, gio.Shader_cover_vert, gio.Shader_cover_frag, pipelines, err := createColorPrograms(ctx, gio.Shader_cover_vert, gio.Shader_cover_frag,
[3]any{c.colUniforms, c.linearGradientUniforms, c.texUniforms}, [3]interface{}{c.colUniforms, c.linearGradientUniforms, c.texUniforms},
) )
if err != nil { if err != nil {
panic(err) panic(err)
@@ -162,7 +162,7 @@ func newCoverer(ctx driver.Device) *coverer {
func newStenciler(ctx driver.Device) *stenciler { func newStenciler(ctx driver.Device) *stenciler {
// Allocate a suitably large index buffer for drawing paths. // Allocate a suitably large index buffer for drawing paths.
indices := make([]uint16, pathBatchSize*6) indices := make([]uint16, pathBatchSize*6)
for i := range pathBatchSize { for i := 0; i < pathBatchSize; i++ {
i := uint16(i) i := uint16(i)
indices[i*6+0] = i*4 + 0 indices[i*6+0] = i*4 + 0
indices[i*6+1] = i*4 + 1 indices[i*6+1] = i*4 + 1
@@ -334,7 +334,7 @@ func (p *pather) begin(sizes []image.Point) {
p.stenciler.begin(sizes) p.stenciler.begin(sizes)
} }
func (p *pather) stencilPath(bounds image.Rectangle, offset image.Point, uv image.Point, data pathData) { func (p *pather) stencilPath(bounds image.Rectangle, offset f32.Point, uv image.Point, data pathData) {
p.stenciler.stencilPath(bounds, offset, uv, data) p.stenciler.stencilPath(bounds, offset, uv, data)
} }
@@ -353,14 +353,14 @@ func (s *stenciler) begin(sizes []image.Point) {
s.fbos.resize(s.ctx, driver.TextureFormatFloat, sizes) s.fbos.resize(s.ctx, driver.TextureFormatFloat, sizes)
} }
func (s *stenciler) stencilPath(bounds image.Rectangle, offset image.Point, uv image.Point, data pathData) { func (s *stenciler) stencilPath(bounds image.Rectangle, offset f32.Point, uv image.Point, data pathData) {
s.ctx.Viewport(uv.X, uv.Y, bounds.Dx(), bounds.Dy()) s.ctx.Viewport(uv.X, uv.Y, bounds.Dx(), bounds.Dy())
// Transform UI coordinates to OpenGL coordinates. // Transform UI coordinates to OpenGL coordinates.
texSize := f32.Point{X: float32(bounds.Dx()), Y: float32(bounds.Dy())} texSize := f32.Point{X: float32(bounds.Dx()), Y: float32(bounds.Dy())}
scale := f32.Point{X: 2 / texSize.X, Y: 2 / texSize.Y} scale := f32.Point{X: 2 / texSize.X, Y: 2 / texSize.Y}
orig := f32.Point{X: -1 - float32(bounds.Min.X)*2/texSize.X, Y: -1 - float32(bounds.Min.Y)*2/texSize.Y} orig := f32.Point{X: -1 - float32(bounds.Min.X)*2/texSize.X, Y: -1 - float32(bounds.Min.Y)*2/texSize.Y}
s.pipeline.uniforms.transform = [4]float32{scale.X, scale.Y, orig.X, orig.Y} s.pipeline.uniforms.transform = [4]float32{scale.X, scale.Y, orig.X, orig.Y}
s.pipeline.uniforms.pathOffset = [2]float32{float32(offset.X), float32(offset.Y)} s.pipeline.uniforms.pathOffset = [2]float32{offset.X, offset.Y}
s.pipeline.pipeline.UploadUniforms(s.ctx) s.pipeline.pipeline.UploadUniforms(s.ctx)
// Draw in batches that fit in uint16 indices. // Draw in batches that fit in uint16 indices.
start := 0 start := 0
+2 -2
View File
@@ -10,7 +10,7 @@ import (
) )
// Struct returns a byte slice view of a struct. // Struct returns a byte slice view of a struct.
func Struct(s any) []byte { func Struct(s interface{}) []byte {
v := reflect.ValueOf(s) v := reflect.ValueOf(s)
sz := int(v.Elem().Type().Size()) sz := int(v.Elem().Type().Size())
return unsafe.Slice((*byte)(unsafe.Pointer(v.Pointer())), sz) return unsafe.Slice((*byte)(unsafe.Pointer(v.Pointer())), sz)
@@ -27,7 +27,7 @@ func Uint32(s []uint32) []byte {
} }
// Slice returns a byte slice view of a slice. // Slice returns a byte slice view of a slice.
func Slice(s any) []byte { func Slice(s interface{}) []byte {
v := reflect.ValueOf(s) v := reflect.ValueOf(s)
first := v.Index(0) first := v.Index(0)
sz := int(first.Type().Size()) sz := int(first.Type().Size())
+6 -2
View File
@@ -9,7 +9,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"runtime" "runtime"
"slices"
"strings" "strings"
"gioui.org/gpu" "gioui.org/gpu"
@@ -155,7 +154,12 @@ func (c *Context) EnableVSync(enable bool) {
} }
func hasExtension(exts []string, ext string) bool { func hasExtension(exts []string, ext string) bool {
return slices.Contains(exts, ext) for _, e := range exts {
if ext == e {
return true
}
}
return false
} }
func createContext(disp _EGLDisplay) (*eglContext, error) { func createContext(disp _EGLDisplay) (*eglContext, error) {
+27 -49
View File
@@ -9,6 +9,8 @@ import (
"unsafe" "unsafe"
syscall "golang.org/x/sys/windows" syscall "golang.org/x/sys/windows"
"gioui.org/internal/gl"
) )
type ( type (
@@ -22,23 +24,23 @@ type (
) )
var ( var (
libEGL = syscall.DLL{} libEGL = syscall.NewLazyDLL("libEGL.dll")
_eglChooseConfig *syscall.Proc _eglChooseConfig = libEGL.NewProc("eglChooseConfig")
_eglCreateContext *syscall.Proc _eglCreateContext = libEGL.NewProc("eglCreateContext")
_eglCreateWindowSurface *syscall.Proc _eglCreateWindowSurface = libEGL.NewProc("eglCreateWindowSurface")
_eglDestroyContext *syscall.Proc _eglDestroyContext = libEGL.NewProc("eglDestroyContext")
_eglDestroySurface *syscall.Proc _eglDestroySurface = libEGL.NewProc("eglDestroySurface")
_eglGetConfigAttrib *syscall.Proc _eglGetConfigAttrib = libEGL.NewProc("eglGetConfigAttrib")
_eglGetDisplay *syscall.Proc _eglGetDisplay = libEGL.NewProc("eglGetDisplay")
_eglGetError *syscall.Proc _eglGetError = libEGL.NewProc("eglGetError")
_eglInitialize *syscall.Proc _eglInitialize = libEGL.NewProc("eglInitialize")
_eglMakeCurrent *syscall.Proc _eglMakeCurrent = libEGL.NewProc("eglMakeCurrent")
_eglReleaseThread *syscall.Proc _eglReleaseThread = libEGL.NewProc("eglReleaseThread")
_eglSwapInterval *syscall.Proc _eglSwapInterval = libEGL.NewProc("eglSwapInterval")
_eglSwapBuffers *syscall.Proc _eglSwapBuffers = libEGL.NewProc("eglSwapBuffers")
_eglTerminate *syscall.Proc _eglTerminate = libEGL.NewProc("eglTerminate")
_eglQueryString *syscall.Proc _eglQueryString = libEGL.NewProc("eglQueryString")
_eglWaitClient *syscall.Proc _eglWaitClient = libEGL.NewProc("eglWaitClient")
) )
var loadOnce sync.Once var loadOnce sync.Once
@@ -52,45 +54,21 @@ func loadEGL() error {
} }
func loadDLLs() error { func loadDLLs() error {
if err := loadDLL(&libEGL, "libEGL.dll"); err != nil { if err := loadDLL(libEGL, "libEGL.dll"); err != nil {
return err return err
} }
if err := loadDLL(gl.LibGLESv2, "libGLESv2.dll"); err != nil {
procs := map[string]**syscall.Proc{ return err
"eglChooseConfig": &_eglChooseConfig,
"eglCreateContext": &_eglCreateContext,
"eglCreateWindowSurface": &_eglCreateWindowSurface,
"eglDestroyContext": &_eglDestroyContext,
"eglDestroySurface": &_eglDestroySurface,
"eglGetConfigAttrib": &_eglGetConfigAttrib,
"eglGetDisplay": &_eglGetDisplay,
"eglGetError": &_eglGetError,
"eglInitialize": &_eglInitialize,
"eglMakeCurrent": &_eglMakeCurrent,
"eglReleaseThread": &_eglReleaseThread,
"eglSwapInterval": &_eglSwapInterval,
"eglSwapBuffers": &_eglSwapBuffers,
"eglTerminate": &_eglTerminate,
"eglQueryString": &_eglQueryString,
"eglWaitClient": &_eglWaitClient,
} }
for name, proc := range procs { // d3dcompiler_47.dll is needed internally for shader compilation to function.
p, err := libEGL.FindProc(name) return loadDLL(syscall.NewLazyDLL("d3dcompiler_47.dll"), "d3dcompiler_47.dll")
if err != nil {
return fmt.Errorf("failed to locate %s in %s: %w", name, libEGL.Name, err)
}
*proc = p
}
return nil
} }
func loadDLL(dll *syscall.DLL, name string) error { func loadDLL(dll *syscall.LazyDLL, name string) error {
handle, err := syscall.LoadLibraryEx(name, 0, syscall.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS) err := dll.Load()
if err != nil { if err != nil {
return fmt.Errorf("egl: failed to load %s: %v", name, err) return fmt.Errorf("egl: failed to load %s: %v", name, err)
} }
dll.Handle = handle
dll.Name = name
return nil return nil
} }
@@ -186,6 +164,6 @@ func eglWaitClient() bool {
// issue34474KeepAlive calls runtime.KeepAlive as a // issue34474KeepAlive calls runtime.KeepAlive as a
// workaround for golang.org/issue/34474. // workaround for golang.org/issue/34474.
func issue34474KeepAlive(v any) { func issue34474KeepAlive(v interface{}) {
runtime.KeepAlive(v) runtime.KeepAlive(v)
} }
-2
View File
@@ -19,8 +19,6 @@ type Affine2D = f32.Affine2D
var NewAffine2D = f32.NewAffine2D var NewAffine2D = f32.NewAffine2D
var AffineId = f32.AffineId
// A Rectangle contains the points (X, Y) where Min.X <= X < Max.X, // A Rectangle contains the points (X, Y) where Min.X <= X < Max.X,
// Min.Y <= Y < Max.Y. // Min.Y <= Y < Max.Y.
type Rectangle struct { type Rectangle struct {
+2 -2
View File
@@ -16,7 +16,7 @@ func main() {
flag.Parse() flag.Parse()
var b bytes.Buffer var b bytes.Buffer
printf := func(content string, args ...any) { printf := func(content string, args ...interface{}) {
fmt.Fprintf(&b, content, args...) fmt.Fprintf(&b, content, args...)
} }
@@ -42,7 +42,7 @@ func main() {
panic(err) panic(err)
} }
err = os.WriteFile(*out, data, 0o755) err = os.WriteFile(*out, data, 0755)
if err != nil { if err != nil {
panic(err) panic(err)
} }
+3 -3
View File
@@ -41,17 +41,17 @@ var sink RGBA
func BenchmarkLinearFromSRGB(b *testing.B) { func BenchmarkLinearFromSRGB(b *testing.B) {
b.Run("opaque", func(b *testing.B) { b.Run("opaque", func(b *testing.B) {
for i := 0; b.Loop(); i++ { for i := 0; i < b.N; i++ {
sink = LinearFromSRGB(color.NRGBA{R: byte(i), G: byte(i >> 8), B: byte(i >> 16), A: 0xFF}) sink = LinearFromSRGB(color.NRGBA{R: byte(i), G: byte(i >> 8), B: byte(i >> 16), A: 0xFF})
} }
}) })
b.Run("translucent", func(b *testing.B) { b.Run("translucent", func(b *testing.B) {
for i := 0; b.Loop(); i++ { for i := 0; i < b.N; i++ {
sink = LinearFromSRGB(color.NRGBA{R: byte(i), G: byte(i >> 8), B: byte(i >> 16), A: 0x50}) sink = LinearFromSRGB(color.NRGBA{R: byte(i), G: byte(i >> 8), B: byte(i >> 16), A: 0x50})
} }
}) })
b.Run("transparent", func(b *testing.B) { b.Run("transparent", func(b *testing.B) {
for i := 0; b.Loop(); i++ { for i := 0; i < b.N; i++ {
sink = LinearFromSRGB(color.NRGBA{R: byte(i), G: byte(i >> 8), B: byte(i >> 16), A: 0x00}) sink = LinearFromSRGB(color.NRGBA{R: byte(i), G: byte(i >> 8), B: byte(i >> 16), A: 0x00})
} }
}) })
+15 -15
View File
@@ -90,7 +90,7 @@ func (e *Extrapolation) Estimate() Estimate {
first := e.get(0) first := e.get(0)
t := first.t t := first.t
// Walk backwards collecting samples. // Walk backwards collecting samples.
for i := range e.samples { for i := 0; i < len(e.samples); i++ {
p := e.get(-i) p := e.get(-i)
age := first.t - p.t age := first.t - p.t
if age >= maxAge || t-p.t >= maxSampleGap { if age >= maxAge || t-p.t >= maxSampleGap {
@@ -172,9 +172,9 @@ func decomposeQR(A *matrix) (*matrix, *matrix, bool) {
// https://en.wikipedia.org/wiki/Gram%E2%80%93Schmidt_process // https://en.wikipedia.org/wiki/Gram%E2%80%93Schmidt_process
Q := newMatrix(A.rows, A.cols) // Column-major. Q := newMatrix(A.rows, A.cols) // Column-major.
Rt := newMatrix(A.rows, A.rows) // R transposed, row-major. Rt := newMatrix(A.rows, A.rows) // R transposed, row-major.
for i := range Q.rows { for i := 0; i < Q.rows; i++ {
// Copy A column. // Copy A column.
for j := range Q.cols { for j := 0; j < Q.cols; j++ {
Q.set(i, j, A.get(i, j)) Q.set(i, j, A.get(i, j))
} }
// Subtract projections. Note that int the projection // Subtract projections. Note that int the projection
@@ -184,9 +184,9 @@ func decomposeQR(A *matrix) (*matrix, *matrix, bool) {
// the normalized column e replaces u, where <e, e> = 1: // the normalized column e replaces u, where <e, e> = 1:
// //
// proje a = <e, a>/<e, e> e = <e, a> e // proje a = <e, a>/<e, e> e = <e, a> e
for j := range i { for j := 0; j < i; j++ {
d := dot(Q.col(j), Q.col(i)) d := dot(Q.col(j), Q.col(i))
for k := range Q.cols { for k := 0; k < Q.cols; k++ {
Q.set(i, k, Q.get(i, k)-d*Q.get(j, k)) Q.set(i, k, Q.get(i, k)-d*Q.get(j, k))
} }
} }
@@ -197,7 +197,7 @@ func decomposeQR(A *matrix) (*matrix, *matrix, bool) {
return nil, nil, false return nil, nil, false
} }
invNorm := 1 / n invNorm := 1 / n
for j := range Q.cols { for j := 0; j < Q.cols; j++ {
Q.set(i, j, Q.get(i, j)*invNorm) Q.set(i, j, Q.get(i, j)*invNorm)
} }
// Update Rt. // Update Rt.
@@ -261,8 +261,8 @@ func (m *matrix) approxEqual(m2 *matrix) bool {
return false return false
} }
const epsilon = 0.00001 const epsilon = 0.00001
for row := range m.rows { for row := 0; row < m.rows; row++ {
for col := range m.cols { for col := 0; col < m.cols; col++ {
d := m2.get(row, col) - m.get(row, col) d := m2.get(row, col) - m.get(row, col)
if d < -epsilon || d > epsilon { if d < -epsilon || d > epsilon {
return false return false
@@ -278,8 +278,8 @@ func (m *matrix) transpose() *matrix {
cols: m.rows, cols: m.rows,
data: make([]float32, len(m.data)), data: make([]float32, len(m.data)),
} }
for i := range m.rows { for i := 0; i < m.rows; i++ {
for j := range m.cols { for j := 0; j < m.cols; j++ {
t.set(j, i, m.get(i, j)) t.set(j, i, m.get(i, j))
} }
} }
@@ -295,10 +295,10 @@ func (m *matrix) mul(m2 *matrix) *matrix {
cols: m2.cols, cols: m2.cols,
data: make([]float32, m.rows*m2.cols), data: make([]float32, m.rows*m2.cols),
} }
for i := range mm.rows { for i := 0; i < mm.rows; i++ {
for j := range mm.cols { for j := 0; j < mm.cols; j++ {
var v float32 var v float32
for k := range m.rows { for k := 0; k < m.rows; k++ {
v += m.get(k, j) * m2.get(i, k) v += m.get(k, j) * m2.get(i, k)
} }
mm.set(i, j, v) mm.set(i, j, v)
@@ -309,8 +309,8 @@ func (m *matrix) mul(m2 *matrix) *matrix {
func (m *matrix) String() string { func (m *matrix) String() string {
var b strings.Builder var b strings.Builder
for i := range m.rows { for i := 0; i < m.rows; i++ {
for j := range m.cols { for j := 0; j < m.cols; j++ {
v := m.get(i, j) v := m.get(i, j)
b.WriteString(strconv.FormatFloat(float64(v), 'g', -1, 32)) b.WriteString(strconv.FormatFloat(float64(v), 'g', -1, 32))
b.WriteString(", ") b.WriteString(", ")
-95
View File
@@ -247,11 +247,9 @@ func (f *Functions) getExtension(name string) js.Value {
func (f *Functions) ActiveTexture(t Enum) { func (f *Functions) ActiveTexture(t Enum) {
f._activeTexture.Invoke(int(t)) f._activeTexture.Invoke(int(t))
} }
func (f *Functions) AttachShader(p Program, s Shader) { func (f *Functions) AttachShader(p Program, s Shader) {
f._attachShader.Invoke(js.Value(p), js.Value(s)) f._attachShader.Invoke(js.Value(p), js.Value(s))
} }
func (f *Functions) BeginQuery(target Enum, query Query) { func (f *Functions) BeginQuery(target Enum, query Query) {
if !f.EXT_disjoint_timer_query_webgl2.IsNull() { if !f.EXT_disjoint_timer_query_webgl2.IsNull() {
f._beginQuery.Invoke(int(target), js.Value(query)) f._beginQuery.Invoke(int(target), js.Value(query))
@@ -259,47 +257,36 @@ func (f *Functions) BeginQuery(target Enum, query Query) {
f.EXT_disjoint_timer_query.Call("beginQueryEXT", int(target), js.Value(query)) f.EXT_disjoint_timer_query.Call("beginQueryEXT", int(target), js.Value(query))
} }
} }
func (f *Functions) BindAttribLocation(p Program, a Attrib, name string) { func (f *Functions) BindAttribLocation(p Program, a Attrib, name string) {
f._bindAttribLocation.Invoke(js.Value(p), int(a), name) f._bindAttribLocation.Invoke(js.Value(p), int(a), name)
} }
func (f *Functions) BindBuffer(target Enum, b Buffer) { func (f *Functions) BindBuffer(target Enum, b Buffer) {
f._bindBuffer.Invoke(int(target), js.Value(b)) f._bindBuffer.Invoke(int(target), js.Value(b))
} }
func (f *Functions) BindBufferBase(target Enum, index int, b Buffer) { func (f *Functions) BindBufferBase(target Enum, index int, b Buffer) {
f._bindBufferBase.Invoke(int(target), index, js.Value(b)) f._bindBufferBase.Invoke(int(target), index, js.Value(b))
} }
func (f *Functions) BindFramebuffer(target Enum, fb Framebuffer) { func (f *Functions) BindFramebuffer(target Enum, fb Framebuffer) {
f._bindFramebuffer.Invoke(int(target), js.Value(fb)) f._bindFramebuffer.Invoke(int(target), js.Value(fb))
} }
func (f *Functions) BindRenderbuffer(target Enum, rb Renderbuffer) { func (f *Functions) BindRenderbuffer(target Enum, rb Renderbuffer) {
f._bindRenderbuffer.Invoke(int(target), js.Value(rb)) f._bindRenderbuffer.Invoke(int(target), js.Value(rb))
} }
func (f *Functions) BindTexture(target Enum, t Texture) { func (f *Functions) BindTexture(target Enum, t Texture) {
f._bindTexture.Invoke(int(target), js.Value(t)) f._bindTexture.Invoke(int(target), js.Value(t))
} }
func (f *Functions) BindImageTexture(unit int, t Texture, level int, layered bool, layer int, access, format Enum) { func (f *Functions) BindImageTexture(unit int, t Texture, level int, layered bool, layer int, access, format Enum) {
panic("not implemented") panic("not implemented")
} }
func (f *Functions) BindVertexArray(a VertexArray) { func (f *Functions) BindVertexArray(a VertexArray) {
panic("not supported") panic("not supported")
} }
func (f *Functions) BlendEquation(mode Enum) { func (f *Functions) BlendEquation(mode Enum) {
f._blendEquation.Invoke(int(mode)) f._blendEquation.Invoke(int(mode))
} }
func (f *Functions) BlendFuncSeparate(srcRGB, dstRGB, srcA, dstA Enum) { func (f *Functions) BlendFuncSeparate(srcRGB, dstRGB, srcA, dstA Enum) {
f._blendFunc.Invoke(int(srcRGB), int(dstRGB), int(srcA), int(dstA)) f._blendFunc.Invoke(int(srcRGB), int(dstRGB), int(srcA), int(dstA))
} }
func (f *Functions) BufferData(target Enum, size int, usage Enum, data []byte) { func (f *Functions) BufferData(target Enum, size int, usage Enum, data []byte) {
if data == nil { if data == nil {
f._bufferData.Invoke(int(target), size, int(usage)) f._bufferData.Invoke(int(target), size, int(usage))
@@ -310,11 +297,9 @@ func (f *Functions) BufferData(target Enum, size int, usage Enum, data []byte) {
f._bufferData.Invoke(int(target), f.byteArrayOf(data), int(usage)) f._bufferData.Invoke(int(target), f.byteArrayOf(data), int(usage))
} }
} }
func (f *Functions) BufferSubData(target Enum, offset int, src []byte) { func (f *Functions) BufferSubData(target Enum, offset int, src []byte) {
f._bufferSubData.Invoke(int(target), offset, f.byteArrayOf(src)) f._bufferSubData.Invoke(int(target), offset, f.byteArrayOf(src))
} }
func (f *Functions) CheckFramebufferStatus(target Enum) Enum { func (f *Functions) CheckFramebufferStatus(target Enum) Enum {
status := Enum(f._checkFramebufferStatus.Invoke(int(target)).Int()) status := Enum(f._checkFramebufferStatus.Invoke(int(target)).Int())
if status != FRAMEBUFFER_COMPLETE && f.Ctx.Call("isContextLost").Bool() { if status != FRAMEBUFFER_COMPLETE && f.Ctx.Call("isContextLost").Bool() {
@@ -323,71 +308,54 @@ func (f *Functions) CheckFramebufferStatus(target Enum) Enum {
} }
return status return status
} }
func (f *Functions) Clear(mask Enum) { func (f *Functions) Clear(mask Enum) {
f._clear.Invoke(int(mask)) f._clear.Invoke(int(mask))
} }
func (f *Functions) ClearColor(red, green, blue, alpha float32) { func (f *Functions) ClearColor(red, green, blue, alpha float32) {
f._clearColor.Invoke(red, green, blue, alpha) f._clearColor.Invoke(red, green, blue, alpha)
} }
func (f *Functions) ClearDepthf(d float32) { func (f *Functions) ClearDepthf(d float32) {
f._clearDepth.Invoke(d) f._clearDepth.Invoke(d)
} }
func (f *Functions) CompileShader(s Shader) { func (f *Functions) CompileShader(s Shader) {
f._compileShader.Invoke(js.Value(s)) f._compileShader.Invoke(js.Value(s))
} }
func (f *Functions) CopyTexSubImage2D(target Enum, level, xoffset, yoffset, x, y, width, height int) { func (f *Functions) CopyTexSubImage2D(target Enum, level, xoffset, yoffset, x, y, width, height int) {
f._copyTexSubImage2D.Invoke(int(target), level, xoffset, yoffset, x, y, width, height) f._copyTexSubImage2D.Invoke(int(target), level, xoffset, yoffset, x, y, width, height)
} }
func (f *Functions) CreateBuffer() Buffer { func (f *Functions) CreateBuffer() Buffer {
return Buffer(f._createBuffer.Invoke()) return Buffer(f._createBuffer.Invoke())
} }
func (f *Functions) CreateFramebuffer() Framebuffer { func (f *Functions) CreateFramebuffer() Framebuffer {
return Framebuffer(f._createFramebuffer.Invoke()) return Framebuffer(f._createFramebuffer.Invoke())
} }
func (f *Functions) CreateProgram() Program { func (f *Functions) CreateProgram() Program {
return Program(f._createProgram.Invoke()) return Program(f._createProgram.Invoke())
} }
func (f *Functions) CreateQuery() Query { func (f *Functions) CreateQuery() Query {
return Query(f._createQuery.Invoke()) return Query(f._createQuery.Invoke())
} }
func (f *Functions) CreateRenderbuffer() Renderbuffer { func (f *Functions) CreateRenderbuffer() Renderbuffer {
return Renderbuffer(f._createRenderbuffer.Invoke()) return Renderbuffer(f._createRenderbuffer.Invoke())
} }
func (f *Functions) CreateShader(ty Enum) Shader { func (f *Functions) CreateShader(ty Enum) Shader {
return Shader(f._createShader.Invoke(int(ty))) return Shader(f._createShader.Invoke(int(ty)))
} }
func (f *Functions) CreateTexture() Texture { func (f *Functions) CreateTexture() Texture {
return Texture(f._createTexture.Invoke()) return Texture(f._createTexture.Invoke())
} }
func (f *Functions) CreateVertexArray() VertexArray { func (f *Functions) CreateVertexArray() VertexArray {
panic("not supported") panic("not supported")
} }
func (f *Functions) DeleteBuffer(v Buffer) { func (f *Functions) DeleteBuffer(v Buffer) {
f._deleteBuffer.Invoke(js.Value(v)) f._deleteBuffer.Invoke(js.Value(v))
} }
func (f *Functions) DeleteFramebuffer(v Framebuffer) { func (f *Functions) DeleteFramebuffer(v Framebuffer) {
f._deleteFramebuffer.Invoke(js.Value(v)) f._deleteFramebuffer.Invoke(js.Value(v))
} }
func (f *Functions) DeleteProgram(p Program) { func (f *Functions) DeleteProgram(p Program) {
f._deleteProgram.Invoke(js.Value(p)) f._deleteProgram.Invoke(js.Value(p))
} }
func (f *Functions) DeleteQuery(query Query) { func (f *Functions) DeleteQuery(query Query) {
if !f.EXT_disjoint_timer_query_webgl2.IsNull() { if !f.EXT_disjoint_timer_query_webgl2.IsNull() {
f._deleteQuery.Invoke(js.Value(query)) f._deleteQuery.Invoke(js.Value(query))
@@ -395,59 +363,45 @@ func (f *Functions) DeleteQuery(query Query) {
f.EXT_disjoint_timer_query.Call("deleteQueryEXT", js.Value(query)) f.EXT_disjoint_timer_query.Call("deleteQueryEXT", js.Value(query))
} }
} }
func (f *Functions) DeleteShader(s Shader) { func (f *Functions) DeleteShader(s Shader) {
f._deleteShader.Invoke(js.Value(s)) f._deleteShader.Invoke(js.Value(s))
} }
func (f *Functions) DeleteRenderbuffer(v Renderbuffer) { func (f *Functions) DeleteRenderbuffer(v Renderbuffer) {
f._deleteRenderbuffer.Invoke(js.Value(v)) f._deleteRenderbuffer.Invoke(js.Value(v))
} }
func (f *Functions) DeleteTexture(v Texture) { func (f *Functions) DeleteTexture(v Texture) {
f._deleteTexture.Invoke(js.Value(v)) f._deleteTexture.Invoke(js.Value(v))
} }
func (f *Functions) DeleteVertexArray(a VertexArray) { func (f *Functions) DeleteVertexArray(a VertexArray) {
panic("not implemented") panic("not implemented")
} }
func (f *Functions) DepthFunc(fn Enum) { func (f *Functions) DepthFunc(fn Enum) {
f._depthFunc.Invoke(int(fn)) f._depthFunc.Invoke(int(fn))
} }
func (f *Functions) DepthMask(mask bool) { func (f *Functions) DepthMask(mask bool) {
f._depthMask.Invoke(mask) f._depthMask.Invoke(mask)
} }
func (f *Functions) DisableVertexAttribArray(a Attrib) { func (f *Functions) DisableVertexAttribArray(a Attrib) {
f._disableVertexAttribArray.Invoke(int(a)) f._disableVertexAttribArray.Invoke(int(a))
} }
func (f *Functions) Disable(cap Enum) { func (f *Functions) Disable(cap Enum) {
f._disable.Invoke(int(cap)) f._disable.Invoke(int(cap))
} }
func (f *Functions) DrawArrays(mode Enum, first, count int) { func (f *Functions) DrawArrays(mode Enum, first, count int) {
f._drawArrays.Invoke(int(mode), first, count) f._drawArrays.Invoke(int(mode), first, count)
} }
func (f *Functions) DrawElements(mode Enum, count int, ty Enum, offset int) { func (f *Functions) DrawElements(mode Enum, count int, ty Enum, offset int) {
f._drawElements.Invoke(int(mode), count, int(ty), offset) f._drawElements.Invoke(int(mode), count, int(ty), offset)
} }
func (f *Functions) DispatchCompute(x, y, z int) { func (f *Functions) DispatchCompute(x, y, z int) {
panic("not implemented") panic("not implemented")
} }
func (f *Functions) Enable(cap Enum) { func (f *Functions) Enable(cap Enum) {
f._enable.Invoke(int(cap)) f._enable.Invoke(int(cap))
} }
func (f *Functions) EnableVertexAttribArray(a Attrib) { func (f *Functions) EnableVertexAttribArray(a Attrib) {
f._enableVertexAttribArray.Invoke(int(a)) f._enableVertexAttribArray.Invoke(int(a))
} }
func (f *Functions) EndQuery(target Enum) { func (f *Functions) EndQuery(target Enum) {
if !f.EXT_disjoint_timer_query_webgl2.IsNull() { if !f.EXT_disjoint_timer_query_webgl2.IsNull() {
f._endQuery.Invoke(int(target)) f._endQuery.Invoke(int(target))
@@ -455,36 +409,28 @@ func (f *Functions) EndQuery(target Enum) {
f.EXT_disjoint_timer_query.Call("endQueryEXT", int(target)) f.EXT_disjoint_timer_query.Call("endQueryEXT", int(target))
} }
} }
func (f *Functions) Finish() { func (f *Functions) Finish() {
f._finish.Invoke() f._finish.Invoke()
} }
func (f *Functions) Flush() { func (f *Functions) Flush() {
f._flush.Invoke() f._flush.Invoke()
} }
func (f *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarget Enum, renderbuffer Renderbuffer) { func (f *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarget Enum, renderbuffer Renderbuffer) {
f._framebufferRenderbuffer.Invoke(int(target), int(attachment), int(renderbuffertarget), js.Value(renderbuffer)) f._framebufferRenderbuffer.Invoke(int(target), int(attachment), int(renderbuffertarget), js.Value(renderbuffer))
} }
func (f *Functions) FramebufferTexture2D(target, attachment, texTarget Enum, t Texture, level int) { func (f *Functions) FramebufferTexture2D(target, attachment, texTarget Enum, t Texture, level int) {
f._framebufferTexture2D.Invoke(int(target), int(attachment), int(texTarget), js.Value(t), level) f._framebufferTexture2D.Invoke(int(target), int(attachment), int(texTarget), js.Value(t), level)
} }
func (f *Functions) GenerateMipmap(target Enum) { func (f *Functions) GenerateMipmap(target Enum) {
f._generateMipmap.Invoke(int(target)) f._generateMipmap.Invoke(int(target))
} }
func (f *Functions) GetError() Enum { func (f *Functions) GetError() Enum {
// Avoid slow getError calls. See gio#179. // Avoid slow getError calls. See gio#179.
return 0 return 0
} }
func (f *Functions) GetRenderbufferParameteri(target, pname Enum) int { func (f *Functions) GetRenderbufferParameteri(target, pname Enum) int {
return paramVal(f._getRenderbufferParameteri.Invoke(int(pname))) return paramVal(f._getRenderbufferParameteri.Invoke(int(pname)))
} }
func (f *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname Enum) int { func (f *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname Enum) int {
if !f.isWebGL2 && pname == FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING { if !f.isWebGL2 && pname == FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING {
// FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING is only available on WebGL 2 // FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING is only available on WebGL 2
@@ -492,7 +438,6 @@ func (f *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname
} }
return paramVal(f._getFramebufferAttachmentParameter.Invoke(int(target), int(attachment), int(pname))) return paramVal(f._getFramebufferAttachmentParameter.Invoke(int(target), int(attachment), int(pname)))
} }
func (f *Functions) GetBinding(pname Enum) Object { func (f *Functions) GetBinding(pname Enum) Object {
obj := f._getParameter.Invoke(int(pname)) obj := f._getParameter.Invoke(int(pname))
if !obj.Truthy() { if !obj.Truthy() {
@@ -500,7 +445,6 @@ func (f *Functions) GetBinding(pname Enum) Object {
} }
return Object(obj) return Object(obj)
} }
func (f *Functions) GetBindingi(pname Enum, idx int) Object { func (f *Functions) GetBindingi(pname Enum, idx int) Object {
obj := f._getIndexedParameter.Invoke(int(pname), idx) obj := f._getIndexedParameter.Invoke(int(pname), idx)
if !obj.Truthy() { if !obj.Truthy() {
@@ -508,7 +452,6 @@ func (f *Functions) GetBindingi(pname Enum, idx int) Object {
} }
return Object(obj) return Object(obj)
} }
func (f *Functions) GetInteger(pname Enum) int { func (f *Functions) GetInteger(pname Enum) int {
if !f.isWebGL2 { if !f.isWebGL2 {
switch pname { switch pname {
@@ -518,11 +461,9 @@ func (f *Functions) GetInteger(pname Enum) int {
} }
return paramVal(f._getParameter.Invoke(int(pname))) return paramVal(f._getParameter.Invoke(int(pname)))
} }
func (f *Functions) GetFloat(pname Enum) float32 { func (f *Functions) GetFloat(pname Enum) float32 {
return float32(f._getParameter.Invoke(int(pname)).Float()) return float32(f._getParameter.Invoke(int(pname)).Float())
} }
func (f *Functions) GetInteger4(pname Enum) [4]int { func (f *Functions) GetInteger4(pname Enum) [4]int {
arr := f._getParameter.Invoke(int(pname)) arr := f._getParameter.Invoke(int(pname))
var res [4]int var res [4]int
@@ -531,7 +472,6 @@ func (f *Functions) GetInteger4(pname Enum) [4]int {
} }
return res return res
} }
func (f *Functions) GetFloat4(pname Enum) [4]float32 { func (f *Functions) GetFloat4(pname Enum) [4]float32 {
arr := f._getParameter.Invoke(int(pname)) arr := f._getParameter.Invoke(int(pname))
var res [4]float32 var res [4]float32
@@ -540,15 +480,12 @@ func (f *Functions) GetFloat4(pname Enum) [4]float32 {
} }
return res return res
} }
func (f *Functions) GetProgrami(p Program, pname Enum) int { func (f *Functions) GetProgrami(p Program, pname Enum) int {
return paramVal(f._getProgramParameter.Invoke(js.Value(p), int(pname))) return paramVal(f._getProgramParameter.Invoke(js.Value(p), int(pname)))
} }
func (f *Functions) GetProgramInfoLog(p Program) string { func (f *Functions) GetProgramInfoLog(p Program) string {
return f._getProgramInfoLog.Invoke(js.Value(p)).String() return f._getProgramInfoLog.Invoke(js.Value(p)).String()
} }
func (f *Functions) GetQueryObjectuiv(query Query, pname Enum) uint { func (f *Functions) GetQueryObjectuiv(query Query, pname Enum) uint {
if !f.EXT_disjoint_timer_query_webgl2.IsNull() { if !f.EXT_disjoint_timer_query_webgl2.IsNull() {
return uint(paramVal(f._getQueryParameter.Invoke(js.Value(query), int(pname)))) return uint(paramVal(f._getQueryParameter.Invoke(js.Value(query), int(pname))))
@@ -556,15 +493,12 @@ func (f *Functions) GetQueryObjectuiv(query Query, pname Enum) uint {
return uint(paramVal(f.EXT_disjoint_timer_query.Call("getQueryObjectEXT", js.Value(query), int(pname)))) return uint(paramVal(f.EXT_disjoint_timer_query.Call("getQueryObjectEXT", js.Value(query), int(pname))))
} }
} }
func (f *Functions) GetShaderi(s Shader, pname Enum) int { func (f *Functions) GetShaderi(s Shader, pname Enum) int {
return paramVal(f._getShaderParameter.Invoke(js.Value(s), int(pname))) return paramVal(f._getShaderParameter.Invoke(js.Value(s), int(pname)))
} }
func (f *Functions) GetShaderInfoLog(s Shader) string { func (f *Functions) GetShaderInfoLog(s Shader) string {
return f._getShaderInfoLog.Invoke(js.Value(s)).String() return f._getShaderInfoLog.Invoke(js.Value(s)).String()
} }
func (f *Functions) GetString(pname Enum) string { func (f *Functions) GetString(pname Enum) string {
switch pname { switch pname {
case EXTENSIONS: case EXTENSIONS:
@@ -578,19 +512,15 @@ func (f *Functions) GetString(pname Enum) string {
return f._getParameter.Invoke(int(pname)).String() return f._getParameter.Invoke(int(pname)).String()
} }
} }
func (f *Functions) GetUniformBlockIndex(p Program, name string) uint { func (f *Functions) GetUniformBlockIndex(p Program, name string) uint {
return uint(paramVal(f._getUniformBlockIndex.Invoke(js.Value(p), name))) return uint(paramVal(f._getUniformBlockIndex.Invoke(js.Value(p), name)))
} }
func (f *Functions) GetUniformLocation(p Program, name string) Uniform { func (f *Functions) GetUniformLocation(p Program, name string) Uniform {
return Uniform(f._getUniformLocation.Invoke(js.Value(p), name)) return Uniform(f._getUniformLocation.Invoke(js.Value(p), name))
} }
func (f *Functions) GetVertexAttrib(index int, pname Enum) int { func (f *Functions) GetVertexAttrib(index int, pname Enum) int {
return paramVal(f._getVertexAttrib.Invoke(index, int(pname))) return paramVal(f._getVertexAttrib.Invoke(index, int(pname)))
} }
func (f *Functions) GetVertexAttribBinding(index int, pname Enum) Object { func (f *Functions) GetVertexAttribBinding(index int, pname Enum) Object {
obj := f._getVertexAttrib.Invoke(index, int(pname)) obj := f._getVertexAttrib.Invoke(index, int(pname))
if !obj.Truthy() { if !obj.Truthy() {
@@ -598,11 +528,9 @@ func (f *Functions) GetVertexAttribBinding(index int, pname Enum) Object {
} }
return Object(obj) return Object(obj)
} }
func (f *Functions) GetVertexAttribPointer(index int, pname Enum) uintptr { func (f *Functions) GetVertexAttribPointer(index int, pname Enum) uintptr {
return uintptr(f._getVertexAttribOffset.Invoke(index, int(pname)).Int()) return uintptr(f._getVertexAttribOffset.Invoke(index, int(pname)).Int())
} }
func (f *Functions) InvalidateFramebuffer(target, attachment Enum) { func (f *Functions) InvalidateFramebuffer(target, attachment Enum) {
fn := f.Ctx.Get("invalidateFramebuffer") fn := f.Ctx.Get("invalidateFramebuffer")
if !fn.IsUndefined() { if !fn.IsUndefined() {
@@ -613,97 +541,74 @@ func (f *Functions) InvalidateFramebuffer(target, attachment Enum) {
f._invalidateFramebuffer.Invoke(int(target), f.int32Buf) f._invalidateFramebuffer.Invoke(int(target), f.int32Buf)
} }
} }
func (f *Functions) IsEnabled(cap Enum) bool { func (f *Functions) IsEnabled(cap Enum) bool {
return f._isEnabled.Invoke(int(cap)).Truthy() return f._isEnabled.Invoke(int(cap)).Truthy()
} }
func (f *Functions) LinkProgram(p Program) { func (f *Functions) LinkProgram(p Program) {
f._linkProgram.Invoke(js.Value(p)) f._linkProgram.Invoke(js.Value(p))
} }
func (f *Functions) PixelStorei(pname Enum, param int) { func (f *Functions) PixelStorei(pname Enum, param int) {
f._pixelStorei.Invoke(int(pname), param) f._pixelStorei.Invoke(int(pname), param)
} }
func (f *Functions) MemoryBarrier(barriers Enum) { func (f *Functions) MemoryBarrier(barriers Enum) {
panic("not implemented") panic("not implemented")
} }
func (f *Functions) MapBufferRange(target Enum, offset, length int, access Enum) []byte { func (f *Functions) MapBufferRange(target Enum, offset, length int, access Enum) []byte {
panic("not implemented") panic("not implemented")
} }
func (f *Functions) RenderbufferStorage(target, internalformat Enum, width, height int) { func (f *Functions) RenderbufferStorage(target, internalformat Enum, width, height int) {
f._renderbufferStorage.Invoke(int(target), int(internalformat), width, height) f._renderbufferStorage.Invoke(int(target), int(internalformat), width, height)
} }
func (f *Functions) ReadPixels(x, y, width, height int, format, ty Enum, data []byte) { func (f *Functions) ReadPixels(x, y, width, height int, format, ty Enum, data []byte) {
ba := f.byteArrayOf(data) ba := f.byteArrayOf(data)
f._readPixels.Invoke(x, y, width, height, int(format), int(ty), ba) f._readPixels.Invoke(x, y, width, height, int(format), int(ty), ba)
js.CopyBytesToGo(data, ba) js.CopyBytesToGo(data, ba)
} }
func (f *Functions) Scissor(x, y, width, height int32) { func (f *Functions) Scissor(x, y, width, height int32) {
f._scissor.Invoke(x, y, width, height) f._scissor.Invoke(x, y, width, height)
} }
func (f *Functions) ShaderSource(s Shader, src string) { func (f *Functions) ShaderSource(s Shader, src string) {
f._shaderSource.Invoke(js.Value(s), src) f._shaderSource.Invoke(js.Value(s), src)
} }
func (f *Functions) TexImage2D(target Enum, level int, internalFormat Enum, width, height int, format, ty Enum) { func (f *Functions) TexImage2D(target Enum, level int, internalFormat Enum, width, height int, format, ty Enum) {
f._texImage2D.Invoke(int(target), int(level), int(internalFormat), int(width), int(height), 0, int(format), int(ty), nil) f._texImage2D.Invoke(int(target), int(level), int(internalFormat), int(width), int(height), 0, int(format), int(ty), nil)
} }
func (f *Functions) TexStorage2D(target Enum, levels int, internalFormat Enum, width, height int) { func (f *Functions) TexStorage2D(target Enum, levels int, internalFormat Enum, width, height int) {
f._texStorage2D.Invoke(int(target), levels, int(internalFormat), width, height) f._texStorage2D.Invoke(int(target), levels, int(internalFormat), width, height)
} }
func (f *Functions) TexSubImage2D(target Enum, level int, x, y, width, height int, format, ty Enum, data []byte) { func (f *Functions) TexSubImage2D(target Enum, level int, x, y, width, height int, format, ty Enum, data []byte) {
f._texSubImage2D.Invoke(int(target), level, x, y, width, height, int(format), int(ty), f.byteArrayOf(data)) f._texSubImage2D.Invoke(int(target), level, x, y, width, height, int(format), int(ty), f.byteArrayOf(data))
} }
func (f *Functions) TexParameteri(target, pname Enum, param int) { func (f *Functions) TexParameteri(target, pname Enum, param int) {
f._texParameteri.Invoke(int(target), int(pname), int(param)) f._texParameteri.Invoke(int(target), int(pname), int(param))
} }
func (f *Functions) UniformBlockBinding(p Program, uniformBlockIndex uint, uniformBlockBinding uint) { func (f *Functions) UniformBlockBinding(p Program, uniformBlockIndex uint, uniformBlockBinding uint) {
f._uniformBlockBinding.Invoke(js.Value(p), int(uniformBlockIndex), int(uniformBlockBinding)) f._uniformBlockBinding.Invoke(js.Value(p), int(uniformBlockIndex), int(uniformBlockBinding))
} }
func (f *Functions) Uniform1f(dst Uniform, v float32) { func (f *Functions) Uniform1f(dst Uniform, v float32) {
f._uniform1f.Invoke(js.Value(dst), v) f._uniform1f.Invoke(js.Value(dst), v)
} }
func (f *Functions) Uniform1i(dst Uniform, v int) { func (f *Functions) Uniform1i(dst Uniform, v int) {
f._uniform1i.Invoke(js.Value(dst), v) f._uniform1i.Invoke(js.Value(dst), v)
} }
func (f *Functions) Uniform2f(dst Uniform, v0, v1 float32) { func (f *Functions) Uniform2f(dst Uniform, v0, v1 float32) {
f._uniform2f.Invoke(js.Value(dst), v0, v1) f._uniform2f.Invoke(js.Value(dst), v0, v1)
} }
func (f *Functions) Uniform3f(dst Uniform, v0, v1, v2 float32) { func (f *Functions) Uniform3f(dst Uniform, v0, v1, v2 float32) {
f._uniform3f.Invoke(js.Value(dst), v0, v1, v2) f._uniform3f.Invoke(js.Value(dst), v0, v1, v2)
} }
func (f *Functions) Uniform4f(dst Uniform, v0, v1, v2, v3 float32) { func (f *Functions) Uniform4f(dst Uniform, v0, v1, v2, v3 float32) {
f._uniform4f.Invoke(js.Value(dst), v0, v1, v2, v3) f._uniform4f.Invoke(js.Value(dst), v0, v1, v2, v3)
} }
func (f *Functions) UseProgram(p Program) { func (f *Functions) UseProgram(p Program) {
f._useProgram.Invoke(js.Value(p)) f._useProgram.Invoke(js.Value(p))
} }
func (f *Functions) UnmapBuffer(target Enum) bool { func (f *Functions) UnmapBuffer(target Enum) bool {
panic("not implemented") panic("not implemented")
} }
func (f *Functions) VertexAttribPointer(dst Attrib, size int, ty Enum, normalized bool, stride, offset int) { func (f *Functions) VertexAttribPointer(dst Attrib, size int, ty Enum, normalized bool, stride, offset int) {
f._vertexAttribPointer.Invoke(int(dst), size, int(ty), normalized, stride, offset) f._vertexAttribPointer.Invoke(int(dst), size, int(ty), normalized, stride, offset)
} }
func (f *Functions) Viewport(x, y, width, height int) { func (f *Functions) Viewport(x, y, width, height int) {
f._viewport.Invoke(x, y, width, height) f._viewport.Invoke(x, y, width, height)
} }
+91 -303
View File
@@ -3,217 +3,103 @@
package gl package gl
import ( import (
"fmt"
"math" "math"
"runtime" "runtime"
"sync"
"syscall" "syscall"
"unsafe" "unsafe"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
) )
func loadGLESv2Procs() error {
dllName := "libGLESv2.dll"
handle, err := windows.LoadLibraryEx(dllName, 0, windows.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)
if err != nil {
return fmt.Errorf("gl: failed to load %s: %v", dllName, err)
}
gles := windows.DLL{Handle: handle, Name: dllName}
// d3dcompiler_47.dll is needed internally for shader compilation to function.
dllName = "d3dcompiler_47.dll"
_, err = windows.LoadLibraryEx(dllName, 0, windows.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)
if err != nil {
return fmt.Errorf("gl: failed to load %s: %v", dllName, err)
}
procs := map[string]**windows.Proc{
"glActiveTexture": &_glActiveTexture,
"glAttachShader": &_glAttachShader,
"glBeginQuery": &_glBeginQuery,
"glBindAttribLocation": &_glBindAttribLocation,
"glBindBuffer": &_glBindBuffer,
"glBindBufferBase": &_glBindBufferBase,
"glBindFramebuffer": &_glBindFramebuffer,
"glBindRenderbuffer": &_glBindRenderbuffer,
"glBindTexture": &_glBindTexture,
"glBindVertexArray": &_glBindVertexArray,
"glBlendEquation": &_glBlendEquation,
"glBlendFuncSeparate": &_glBlendFuncSeparate,
"glBufferData": &_glBufferData,
"glBufferSubData": &_glBufferSubData,
"glCheckFramebufferStatus": &_glCheckFramebufferStatus,
"glClear": &_glClear,
"glClearColor": &_glClearColor,
"glClearDepthf": &_glClearDepthf,
"glDeleteQueries": &_glDeleteQueries,
"glDeleteVertexArrays": &_glDeleteVertexArrays,
"glCompileShader": &_glCompileShader,
"glCopyTexSubImage2D": &_glCopyTexSubImage2D,
"glGenerateMipmap": &_glGenerateMipmap,
"glGenBuffers": &_glGenBuffers,
"glGenFramebuffers": &_glGenFramebuffers,
"glGenVertexArrays": &_glGenVertexArrays,
"glGetUniformBlockIndex": &_glGetUniformBlockIndex,
"glCreateProgram": &_glCreateProgram,
"glGenRenderbuffers": &_glGenRenderbuffers,
"glCreateShader": &_glCreateShader,
"glGenTextures": &_glGenTextures,
"glDeleteBuffers": &_glDeleteBuffers,
"glDeleteFramebuffers": &_glDeleteFramebuffers,
"glDeleteProgram": &_glDeleteProgram,
"glDeleteShader": &_glDeleteShader,
"glDeleteRenderbuffers": &_glDeleteRenderbuffers,
"glDeleteTextures": &_glDeleteTextures,
"glDepthFunc": &_glDepthFunc,
"glDepthMask": &_glDepthMask,
"glDisableVertexAttribArray": &_glDisableVertexAttribArray,
"glDisable": &_glDisable,
"glDrawArrays": &_glDrawArrays,
"glDrawElements": &_glDrawElements,
"glEnable": &_glEnable,
"glEnableVertexAttribArray": &_glEnableVertexAttribArray,
"glEndQuery": &_glEndQuery,
"glFinish": &_glFinish,
"glFlush": &_glFlush,
"glFramebufferRenderbuffer": &_glFramebufferRenderbuffer,
"glFramebufferTexture2D": &_glFramebufferTexture2D,
"glGenQueries": &_glGenQueries,
"glGetError": &_glGetError,
"glGetRenderbufferParameteriv": &_glGetRenderbufferParameteriv,
"glGetFloatv": &_glGetFloatv,
"glGetFramebufferAttachmentParameteriv": &_glGetFramebufferAttachmentParameteriv,
"glGetIntegerv": &_glGetIntegerv,
"glGetIntegeri_v": &_glGetIntegeri_v,
"glGetProgramiv": &_glGetProgramiv,
"glGetProgramInfoLog": &_glGetProgramInfoLog,
"glGetQueryObjectuiv": &_glGetQueryObjectuiv,
"glGetShaderiv": &_glGetShaderiv,
"glGetShaderInfoLog": &_glGetShaderInfoLog,
"glGetString": &_glGetString,
"glGetUniformLocation": &_glGetUniformLocation,
"glGetVertexAttribiv": &_glGetVertexAttribiv,
"glGetVertexAttribPointerv": &_glGetVertexAttribPointerv,
"glInvalidateFramebuffer": &_glInvalidateFramebuffer,
"glIsEnabled": &_glIsEnabled,
"glLinkProgram": &_glLinkProgram,
"glPixelStorei": &_glPixelStorei,
"glReadPixels": &_glReadPixels,
"glRenderbufferStorage": &_glRenderbufferStorage,
"glScissor": &_glScissor,
"glShaderSource": &_glShaderSource,
"glTexImage2D": &_glTexImage2D,
"glTexStorage2D": &_glTexStorage2D,
"glTexSubImage2D": &_glTexSubImage2D,
"glTexParameteri": &_glTexParameteri,
"glUniformBlockBinding": &_glUniformBlockBinding,
"glUniform1f": &_glUniform1f,
"glUniform1i": &_glUniform1i,
"glUniform2f": &_glUniform2f,
"glUniform3f": &_glUniform3f,
"glUniform4f": &_glUniform4f,
"glUseProgram": &_glUseProgram,
"glVertexAttribPointer": &_glVertexAttribPointer,
"glViewport": &_glViewport,
}
for name, proc := range procs {
p, err := gles.FindProc(name)
if err != nil {
return fmt.Errorf("failed to locate %s in %s: %w", name, gles.Name, err)
}
*proc = p
}
return nil
}
var ( var (
glInitOnce sync.Once LibGLESv2 = windows.NewLazyDLL("libGLESv2.dll")
_glActiveTexture *windows.Proc _glActiveTexture = LibGLESv2.NewProc("glActiveTexture")
_glAttachShader *windows.Proc _glAttachShader = LibGLESv2.NewProc("glAttachShader")
_glBeginQuery *windows.Proc _glBeginQuery = LibGLESv2.NewProc("glBeginQuery")
_glBindAttribLocation *windows.Proc _glBindAttribLocation = LibGLESv2.NewProc("glBindAttribLocation")
_glBindBuffer *windows.Proc _glBindBuffer = LibGLESv2.NewProc("glBindBuffer")
_glBindBufferBase *windows.Proc _glBindBufferBase = LibGLESv2.NewProc("glBindBufferBase")
_glBindFramebuffer *windows.Proc _glBindFramebuffer = LibGLESv2.NewProc("glBindFramebuffer")
_glBindRenderbuffer *windows.Proc _glBindRenderbuffer = LibGLESv2.NewProc("glBindRenderbuffer")
_glBindTexture *windows.Proc _glBindTexture = LibGLESv2.NewProc("glBindTexture")
_glBindVertexArray *windows.Proc _glBindVertexArray = LibGLESv2.NewProc("glBindVertexArray")
_glBlendEquation *windows.Proc _glBlendEquation = LibGLESv2.NewProc("glBlendEquation")
_glBlendFuncSeparate *windows.Proc _glBlendFuncSeparate = LibGLESv2.NewProc("glBlendFuncSeparate")
_glBufferData *windows.Proc _glBufferData = LibGLESv2.NewProc("glBufferData")
_glBufferSubData *windows.Proc _glBufferSubData = LibGLESv2.NewProc("glBufferSubData")
_glCheckFramebufferStatus *windows.Proc _glCheckFramebufferStatus = LibGLESv2.NewProc("glCheckFramebufferStatus")
_glClear *windows.Proc _glClear = LibGLESv2.NewProc("glClear")
_glClearColor *windows.Proc _glClearColor = LibGLESv2.NewProc("glClearColor")
_glClearDepthf *windows.Proc _glClearDepthf = LibGLESv2.NewProc("glClearDepthf")
_glDeleteQueries *windows.Proc _glDeleteQueries = LibGLESv2.NewProc("glDeleteQueries")
_glDeleteVertexArrays *windows.Proc _glDeleteVertexArrays = LibGLESv2.NewProc("glDeleteVertexArrays")
_glCompileShader *windows.Proc _glCompileShader = LibGLESv2.NewProc("glCompileShader")
_glCopyTexSubImage2D *windows.Proc _glCopyTexSubImage2D = LibGLESv2.NewProc("glCopyTexSubImage2D")
_glGenerateMipmap *windows.Proc _glGenerateMipmap = LibGLESv2.NewProc("glGenerateMipmap")
_glGenBuffers *windows.Proc _glGenBuffers = LibGLESv2.NewProc("glGenBuffers")
_glGenFramebuffers *windows.Proc _glGenFramebuffers = LibGLESv2.NewProc("glGenFramebuffers")
_glGenVertexArrays *windows.Proc _glGenVertexArrays = LibGLESv2.NewProc("glGenVertexArrays")
_glGetUniformBlockIndex *windows.Proc _glGetUniformBlockIndex = LibGLESv2.NewProc("glGetUniformBlockIndex")
_glCreateProgram *windows.Proc _glCreateProgram = LibGLESv2.NewProc("glCreateProgram")
_glGenRenderbuffers *windows.Proc _glGenRenderbuffers = LibGLESv2.NewProc("glGenRenderbuffers")
_glCreateShader *windows.Proc _glCreateShader = LibGLESv2.NewProc("glCreateShader")
_glGenTextures *windows.Proc _glGenTextures = LibGLESv2.NewProc("glGenTextures")
_glDeleteBuffers *windows.Proc _glDeleteBuffers = LibGLESv2.NewProc("glDeleteBuffers")
_glDeleteFramebuffers *windows.Proc _glDeleteFramebuffers = LibGLESv2.NewProc("glDeleteFramebuffers")
_glDeleteProgram *windows.Proc _glDeleteProgram = LibGLESv2.NewProc("glDeleteProgram")
_glDeleteShader *windows.Proc _glDeleteShader = LibGLESv2.NewProc("glDeleteShader")
_glDeleteRenderbuffers *windows.Proc _glDeleteRenderbuffers = LibGLESv2.NewProc("glDeleteRenderbuffers")
_glDeleteTextures *windows.Proc _glDeleteTextures = LibGLESv2.NewProc("glDeleteTextures")
_glDepthFunc *windows.Proc _glDepthFunc = LibGLESv2.NewProc("glDepthFunc")
_glDepthMask *windows.Proc _glDepthMask = LibGLESv2.NewProc("glDepthMask")
_glDisableVertexAttribArray *windows.Proc _glDisableVertexAttribArray = LibGLESv2.NewProc("glDisableVertexAttribArray")
_glDisable *windows.Proc _glDisable = LibGLESv2.NewProc("glDisable")
_glDrawArrays *windows.Proc _glDrawArrays = LibGLESv2.NewProc("glDrawArrays")
_glDrawElements *windows.Proc _glDrawElements = LibGLESv2.NewProc("glDrawElements")
_glEnable *windows.Proc _glEnable = LibGLESv2.NewProc("glEnable")
_glEnableVertexAttribArray *windows.Proc _glEnableVertexAttribArray = LibGLESv2.NewProc("glEnableVertexAttribArray")
_glEndQuery *windows.Proc _glEndQuery = LibGLESv2.NewProc("glEndQuery")
_glFinish *windows.Proc _glFinish = LibGLESv2.NewProc("glFinish")
_glFlush *windows.Proc _glFlush = LibGLESv2.NewProc("glFlush")
_glFramebufferRenderbuffer *windows.Proc _glFramebufferRenderbuffer = LibGLESv2.NewProc("glFramebufferRenderbuffer")
_glFramebufferTexture2D *windows.Proc _glFramebufferTexture2D = LibGLESv2.NewProc("glFramebufferTexture2D")
_glGenQueries *windows.Proc _glGenQueries = LibGLESv2.NewProc("glGenQueries")
_glGetError *windows.Proc _glGetError = LibGLESv2.NewProc("glGetError")
_glGetRenderbufferParameteriv *windows.Proc _glGetRenderbufferParameteriv = LibGLESv2.NewProc("glGetRenderbufferParameteriv")
_glGetFloatv *windows.Proc _glGetFloatv = LibGLESv2.NewProc("glGetFloatv")
_glGetFramebufferAttachmentParameteriv *windows.Proc _glGetFramebufferAttachmentParameteriv = LibGLESv2.NewProc("glGetFramebufferAttachmentParameteriv")
_glGetIntegerv *windows.Proc _glGetIntegerv = LibGLESv2.NewProc("glGetIntegerv")
_glGetIntegeri_v *windows.Proc _glGetIntegeri_v = LibGLESv2.NewProc("glGetIntegeri_v")
_glGetProgramiv *windows.Proc _glGetProgramiv = LibGLESv2.NewProc("glGetProgramiv")
_glGetProgramInfoLog *windows.Proc _glGetProgramInfoLog = LibGLESv2.NewProc("glGetProgramInfoLog")
_glGetQueryObjectuiv *windows.Proc _glGetQueryObjectuiv = LibGLESv2.NewProc("glGetQueryObjectuiv")
_glGetShaderiv *windows.Proc _glGetShaderiv = LibGLESv2.NewProc("glGetShaderiv")
_glGetShaderInfoLog *windows.Proc _glGetShaderInfoLog = LibGLESv2.NewProc("glGetShaderInfoLog")
_glGetString *windows.Proc _glGetString = LibGLESv2.NewProc("glGetString")
_glGetUniformLocation *windows.Proc _glGetUniformLocation = LibGLESv2.NewProc("glGetUniformLocation")
_glGetVertexAttribiv *windows.Proc _glGetVertexAttribiv = LibGLESv2.NewProc("glGetVertexAttribiv")
_glGetVertexAttribPointerv *windows.Proc _glGetVertexAttribPointerv = LibGLESv2.NewProc("glGetVertexAttribPointerv")
_glInvalidateFramebuffer *windows.Proc _glInvalidateFramebuffer = LibGLESv2.NewProc("glInvalidateFramebuffer")
_glIsEnabled *windows.Proc _glIsEnabled = LibGLESv2.NewProc("glIsEnabled")
_glLinkProgram *windows.Proc _glLinkProgram = LibGLESv2.NewProc("glLinkProgram")
_glPixelStorei *windows.Proc _glPixelStorei = LibGLESv2.NewProc("glPixelStorei")
_glReadPixels *windows.Proc _glReadPixels = LibGLESv2.NewProc("glReadPixels")
_glRenderbufferStorage *windows.Proc _glRenderbufferStorage = LibGLESv2.NewProc("glRenderbufferStorage")
_glScissor *windows.Proc _glScissor = LibGLESv2.NewProc("glScissor")
_glShaderSource *windows.Proc _glShaderSource = LibGLESv2.NewProc("glShaderSource")
_glTexImage2D *windows.Proc _glTexImage2D = LibGLESv2.NewProc("glTexImage2D")
_glTexStorage2D *windows.Proc _glTexStorage2D = LibGLESv2.NewProc("glTexStorage2D")
_glTexSubImage2D *windows.Proc _glTexSubImage2D = LibGLESv2.NewProc("glTexSubImage2D")
_glTexParameteri *windows.Proc _glTexParameteri = LibGLESv2.NewProc("glTexParameteri")
_glUniformBlockBinding *windows.Proc _glUniformBlockBinding = LibGLESv2.NewProc("glUniformBlockBinding")
_glUniform1f *windows.Proc _glUniform1f = LibGLESv2.NewProc("glUniform1f")
_glUniform1i *windows.Proc _glUniform1i = LibGLESv2.NewProc("glUniform1i")
_glUniform2f *windows.Proc _glUniform2f = LibGLESv2.NewProc("glUniform2f")
_glUniform3f *windows.Proc _glUniform3f = LibGLESv2.NewProc("glUniform3f")
_glUniform4f *windows.Proc _glUniform4f = LibGLESv2.NewProc("glUniform4f")
_glUseProgram *windows.Proc _glUseProgram = LibGLESv2.NewProc("glUseProgram")
_glVertexAttribPointer *windows.Proc _glVertexAttribPointer = LibGLESv2.NewProc("glVertexAttribPointer")
_glViewport *windows.Proc _glViewport = LibGLESv2.NewProc("glViewport")
) )
type Functions struct { type Functions struct {
@@ -223,74 +109,57 @@ type Functions struct {
uintptrs [100]uintptr uintptrs [100]uintptr
} }
type Context any type Context interface{}
func NewFunctions(ctx Context, forceES bool) (*Functions, error) { func NewFunctions(ctx Context, forceES bool) (*Functions, error) {
if ctx != nil { if ctx != nil {
panic("non-nil context") panic("non-nil context")
} }
var err error return new(Functions), nil
glInitOnce.Do(func() {
err = loadGLESv2Procs()
})
return new(Functions), err
} }
func (c *Functions) ActiveTexture(t Enum) { func (c *Functions) ActiveTexture(t Enum) {
syscall.Syscall(_glActiveTexture.Addr(), 1, uintptr(t), 0, 0) syscall.Syscall(_glActiveTexture.Addr(), 1, uintptr(t), 0, 0)
} }
func (c *Functions) AttachShader(p Program, s Shader) { func (c *Functions) AttachShader(p Program, s Shader) {
syscall.Syscall(_glAttachShader.Addr(), 2, uintptr(p.V), uintptr(s.V), 0) syscall.Syscall(_glAttachShader.Addr(), 2, uintptr(p.V), uintptr(s.V), 0)
} }
func (f *Functions) BeginQuery(target Enum, query Query) { func (f *Functions) BeginQuery(target Enum, query Query) {
syscall.Syscall(_glBeginQuery.Addr(), 2, uintptr(target), uintptr(query.V), 0) syscall.Syscall(_glBeginQuery.Addr(), 2, uintptr(target), uintptr(query.V), 0)
} }
func (c *Functions) BindAttribLocation(p Program, a Attrib, name string) { func (c *Functions) BindAttribLocation(p Program, a Attrib, name string) {
cname := cString(name) cname := cString(name)
c0 := &cname[0] c0 := &cname[0]
syscall.Syscall(_glBindAttribLocation.Addr(), 3, uintptr(p.V), uintptr(a), uintptr(unsafe.Pointer(c0))) syscall.Syscall(_glBindAttribLocation.Addr(), 3, uintptr(p.V), uintptr(a), uintptr(unsafe.Pointer(c0)))
issue34474KeepAlive(c) issue34474KeepAlive(c)
} }
func (c *Functions) BindBuffer(target Enum, b Buffer) { func (c *Functions) BindBuffer(target Enum, b Buffer) {
syscall.Syscall(_glBindBuffer.Addr(), 2, uintptr(target), uintptr(b.V), 0) syscall.Syscall(_glBindBuffer.Addr(), 2, uintptr(target), uintptr(b.V), 0)
} }
func (c *Functions) BindBufferBase(target Enum, index int, b Buffer) { func (c *Functions) BindBufferBase(target Enum, index int, b Buffer) {
syscall.Syscall(_glBindBufferBase.Addr(), 3, uintptr(target), uintptr(index), uintptr(b.V)) syscall.Syscall(_glBindBufferBase.Addr(), 3, uintptr(target), uintptr(index), uintptr(b.V))
} }
func (c *Functions) BindFramebuffer(target Enum, fb Framebuffer) { func (c *Functions) BindFramebuffer(target Enum, fb Framebuffer) {
syscall.Syscall(_glBindFramebuffer.Addr(), 2, uintptr(target), uintptr(fb.V), 0) syscall.Syscall(_glBindFramebuffer.Addr(), 2, uintptr(target), uintptr(fb.V), 0)
} }
func (c *Functions) BindRenderbuffer(target Enum, rb Renderbuffer) { func (c *Functions) BindRenderbuffer(target Enum, rb Renderbuffer) {
syscall.Syscall(_glBindRenderbuffer.Addr(), 2, uintptr(target), uintptr(rb.V), 0) syscall.Syscall(_glBindRenderbuffer.Addr(), 2, uintptr(target), uintptr(rb.V), 0)
} }
func (f *Functions) BindImageTexture(unit int, t Texture, level int, layered bool, layer int, access, format Enum) { func (f *Functions) BindImageTexture(unit int, t Texture, level int, layered bool, layer int, access, format Enum) {
panic("not implemented") panic("not implemented")
} }
func (c *Functions) BindTexture(target Enum, t Texture) { func (c *Functions) BindTexture(target Enum, t Texture) {
syscall.Syscall(_glBindTexture.Addr(), 2, uintptr(target), uintptr(t.V), 0) syscall.Syscall(_glBindTexture.Addr(), 2, uintptr(target), uintptr(t.V), 0)
} }
func (c *Functions) BindVertexArray(a VertexArray) { func (c *Functions) BindVertexArray(a VertexArray) {
syscall.Syscall(_glBindVertexArray.Addr(), 1, uintptr(a.V), 0, 0) syscall.Syscall(_glBindVertexArray.Addr(), 1, uintptr(a.V), 0, 0)
} }
func (c *Functions) BlendEquation(mode Enum) { func (c *Functions) BlendEquation(mode Enum) {
syscall.Syscall(_glBlendEquation.Addr(), 1, uintptr(mode), 0, 0) syscall.Syscall(_glBlendEquation.Addr(), 1, uintptr(mode), 0, 0)
} }
func (c *Functions) BlendFuncSeparate(srcRGB, dstRGB, srcA, dstA Enum) { func (c *Functions) BlendFuncSeparate(srcRGB, dstRGB, srcA, dstA Enum) {
syscall.Syscall6(_glBlendFuncSeparate.Addr(), 4, uintptr(srcRGB), uintptr(dstRGB), uintptr(srcA), uintptr(dstA), 0, 0) syscall.Syscall6(_glBlendFuncSeparate.Addr(), 4, uintptr(srcRGB), uintptr(dstRGB), uintptr(srcA), uintptr(dstA), 0, 0)
} }
func (c *Functions) BufferData(target Enum, size int, usage Enum, data []byte) { func (c *Functions) BufferData(target Enum, size int, usage Enum, data []byte) {
var p unsafe.Pointer var p unsafe.Pointer
if len(data) > 0 { if len(data) > 0 {
@@ -298,7 +167,6 @@ func (c *Functions) BufferData(target Enum, size int, usage Enum, data []byte) {
} }
syscall.Syscall6(_glBufferData.Addr(), 4, uintptr(target), uintptr(size), uintptr(p), uintptr(usage), 0, 0) syscall.Syscall6(_glBufferData.Addr(), 4, uintptr(target), uintptr(size), uintptr(p), uintptr(usage), 0, 0)
} }
func (f *Functions) BufferSubData(target Enum, offset int, src []byte) { func (f *Functions) BufferSubData(target Enum, offset int, src []byte) {
if n := len(src); n > 0 { if n := len(src); n > 0 {
s0 := &src[0] s0 := &src[0]
@@ -306,118 +174,93 @@ func (f *Functions) BufferSubData(target Enum, offset int, src []byte) {
issue34474KeepAlive(s0) issue34474KeepAlive(s0)
} }
} }
func (c *Functions) CheckFramebufferStatus(target Enum) Enum { func (c *Functions) CheckFramebufferStatus(target Enum) Enum {
s, _, _ := syscall.Syscall(_glCheckFramebufferStatus.Addr(), 1, uintptr(target), 0, 0) s, _, _ := syscall.Syscall(_glCheckFramebufferStatus.Addr(), 1, uintptr(target), 0, 0)
return Enum(s) return Enum(s)
} }
func (c *Functions) Clear(mask Enum) { func (c *Functions) Clear(mask Enum) {
syscall.Syscall(_glClear.Addr(), 1, uintptr(mask), 0, 0) syscall.Syscall(_glClear.Addr(), 1, uintptr(mask), 0, 0)
} }
func (c *Functions) ClearColor(red, green, blue, alpha float32) { func (c *Functions) ClearColor(red, green, blue, alpha float32) {
syscall.Syscall6(_glClearColor.Addr(), 4, uintptr(math.Float32bits(red)), uintptr(math.Float32bits(green)), uintptr(math.Float32bits(blue)), uintptr(math.Float32bits(alpha)), 0, 0) syscall.Syscall6(_glClearColor.Addr(), 4, uintptr(math.Float32bits(red)), uintptr(math.Float32bits(green)), uintptr(math.Float32bits(blue)), uintptr(math.Float32bits(alpha)), 0, 0)
} }
func (c *Functions) ClearDepthf(d float32) { func (c *Functions) ClearDepthf(d float32) {
syscall.Syscall(_glClearDepthf.Addr(), 1, uintptr(math.Float32bits(d)), 0, 0) syscall.Syscall(_glClearDepthf.Addr(), 1, uintptr(math.Float32bits(d)), 0, 0)
} }
func (c *Functions) CompileShader(s Shader) { func (c *Functions) CompileShader(s Shader) {
syscall.Syscall(_glCompileShader.Addr(), 1, uintptr(s.V), 0, 0) syscall.Syscall(_glCompileShader.Addr(), 1, uintptr(s.V), 0, 0)
} }
func (f *Functions) CopyTexSubImage2D(target Enum, level, xoffset, yoffset, x, y, width, height int) { func (f *Functions) CopyTexSubImage2D(target Enum, level, xoffset, yoffset, x, y, width, height int) {
syscall.Syscall9(_glCopyTexSubImage2D.Addr(), 8, uintptr(target), uintptr(level), uintptr(xoffset), uintptr(yoffset), uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0) syscall.Syscall9(_glCopyTexSubImage2D.Addr(), 8, uintptr(target), uintptr(level), uintptr(xoffset), uintptr(yoffset), uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0)
} }
func (f *Functions) GenerateMipmap(target Enum) { func (f *Functions) GenerateMipmap(target Enum) {
syscall.Syscall(_glGenerateMipmap.Addr(), 1, uintptr(target), 0, 0) syscall.Syscall(_glGenerateMipmap.Addr(), 1, uintptr(target), 0, 0)
} }
func (c *Functions) CreateBuffer() Buffer { func (c *Functions) CreateBuffer() Buffer {
var buf uintptr var buf uintptr
syscall.Syscall(_glGenBuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&buf)), 0) syscall.Syscall(_glGenBuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&buf)), 0)
return Buffer{uint(buf)} return Buffer{uint(buf)}
} }
func (c *Functions) CreateFramebuffer() Framebuffer { func (c *Functions) CreateFramebuffer() Framebuffer {
var fb uintptr var fb uintptr
syscall.Syscall(_glGenFramebuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&fb)), 0) syscall.Syscall(_glGenFramebuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&fb)), 0)
return Framebuffer{uint(fb)} return Framebuffer{uint(fb)}
} }
func (c *Functions) CreateProgram() Program { func (c *Functions) CreateProgram() Program {
p, _, _ := syscall.Syscall(_glCreateProgram.Addr(), 0, 0, 0, 0) p, _, _ := syscall.Syscall(_glCreateProgram.Addr(), 0, 0, 0, 0)
return Program{uint(p)} return Program{uint(p)}
} }
func (f *Functions) CreateQuery() Query { func (f *Functions) CreateQuery() Query {
var q uintptr var q uintptr
syscall.Syscall(_glGenQueries.Addr(), 2, 1, uintptr(unsafe.Pointer(&q)), 0) syscall.Syscall(_glGenQueries.Addr(), 2, 1, uintptr(unsafe.Pointer(&q)), 0)
return Query{uint(q)} return Query{uint(q)}
} }
func (c *Functions) CreateRenderbuffer() Renderbuffer { func (c *Functions) CreateRenderbuffer() Renderbuffer {
var rb uintptr var rb uintptr
syscall.Syscall(_glGenRenderbuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&rb)), 0) syscall.Syscall(_glGenRenderbuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&rb)), 0)
return Renderbuffer{uint(rb)} return Renderbuffer{uint(rb)}
} }
func (c *Functions) CreateShader(ty Enum) Shader { func (c *Functions) CreateShader(ty Enum) Shader {
s, _, _ := syscall.Syscall(_glCreateShader.Addr(), 1, uintptr(ty), 0, 0) s, _, _ := syscall.Syscall(_glCreateShader.Addr(), 1, uintptr(ty), 0, 0)
return Shader{uint(s)} return Shader{uint(s)}
} }
func (c *Functions) CreateTexture() Texture { func (c *Functions) CreateTexture() Texture {
var t uintptr var t uintptr
syscall.Syscall(_glGenTextures.Addr(), 2, 1, uintptr(unsafe.Pointer(&t)), 0) syscall.Syscall(_glGenTextures.Addr(), 2, 1, uintptr(unsafe.Pointer(&t)), 0)
return Texture{uint(t)} return Texture{uint(t)}
} }
func (c *Functions) CreateVertexArray() VertexArray { func (c *Functions) CreateVertexArray() VertexArray {
var t uintptr var t uintptr
syscall.Syscall(_glGenVertexArrays.Addr(), 2, 1, uintptr(unsafe.Pointer(&t)), 0) syscall.Syscall(_glGenVertexArrays.Addr(), 2, 1, uintptr(unsafe.Pointer(&t)), 0)
return VertexArray{uint(t)} return VertexArray{uint(t)}
} }
func (c *Functions) DeleteBuffer(v Buffer) { func (c *Functions) DeleteBuffer(v Buffer) {
syscall.Syscall(_glDeleteBuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v)), 0) syscall.Syscall(_glDeleteBuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v)), 0)
} }
func (c *Functions) DeleteFramebuffer(v Framebuffer) { func (c *Functions) DeleteFramebuffer(v Framebuffer) {
syscall.Syscall(_glDeleteFramebuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0) syscall.Syscall(_glDeleteFramebuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0)
} }
func (c *Functions) DeleteProgram(p Program) { func (c *Functions) DeleteProgram(p Program) {
syscall.Syscall(_glDeleteProgram.Addr(), 1, uintptr(p.V), 0, 0) syscall.Syscall(_glDeleteProgram.Addr(), 1, uintptr(p.V), 0, 0)
} }
func (f *Functions) DeleteQuery(query Query) { func (f *Functions) DeleteQuery(query Query) {
syscall.Syscall(_glDeleteQueries.Addr(), 2, 1, uintptr(unsafe.Pointer(&query.V)), 0) syscall.Syscall(_glDeleteQueries.Addr(), 2, 1, uintptr(unsafe.Pointer(&query.V)), 0)
} }
func (c *Functions) DeleteShader(s Shader) { func (c *Functions) DeleteShader(s Shader) {
syscall.Syscall(_glDeleteShader.Addr(), 1, uintptr(s.V), 0, 0) syscall.Syscall(_glDeleteShader.Addr(), 1, uintptr(s.V), 0, 0)
} }
func (c *Functions) DeleteRenderbuffer(v Renderbuffer) { func (c *Functions) DeleteRenderbuffer(v Renderbuffer) {
syscall.Syscall(_glDeleteRenderbuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0) syscall.Syscall(_glDeleteRenderbuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0)
} }
func (c *Functions) DeleteTexture(v Texture) { func (c *Functions) DeleteTexture(v Texture) {
syscall.Syscall(_glDeleteTextures.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0) syscall.Syscall(_glDeleteTextures.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0)
} }
func (f *Functions) DeleteVertexArray(array VertexArray) { func (f *Functions) DeleteVertexArray(array VertexArray) {
syscall.Syscall(_glDeleteVertexArrays.Addr(), 2, 1, uintptr(unsafe.Pointer(&array.V)), 0) syscall.Syscall(_glDeleteVertexArrays.Addr(), 2, 1, uintptr(unsafe.Pointer(&array.V)), 0)
} }
func (c *Functions) DepthFunc(f Enum) { func (c *Functions) DepthFunc(f Enum) {
syscall.Syscall(_glDepthFunc.Addr(), 1, uintptr(f), 0, 0) syscall.Syscall(_glDepthFunc.Addr(), 1, uintptr(f), 0, 0)
} }
func (c *Functions) DepthMask(mask bool) { func (c *Functions) DepthMask(mask bool) {
var m uintptr var m uintptr
if mask { if mask {
@@ -425,55 +268,42 @@ func (c *Functions) DepthMask(mask bool) {
} }
syscall.Syscall(_glDepthMask.Addr(), 1, m, 0, 0) syscall.Syscall(_glDepthMask.Addr(), 1, m, 0, 0)
} }
func (c *Functions) DisableVertexAttribArray(a Attrib) { func (c *Functions) DisableVertexAttribArray(a Attrib) {
syscall.Syscall(_glDisableVertexAttribArray.Addr(), 1, uintptr(a), 0, 0) syscall.Syscall(_glDisableVertexAttribArray.Addr(), 1, uintptr(a), 0, 0)
} }
func (c *Functions) Disable(cap Enum) { func (c *Functions) Disable(cap Enum) {
syscall.Syscall(_glDisable.Addr(), 1, uintptr(cap), 0, 0) syscall.Syscall(_glDisable.Addr(), 1, uintptr(cap), 0, 0)
} }
func (c *Functions) DrawArrays(mode Enum, first, count int) { func (c *Functions) DrawArrays(mode Enum, first, count int) {
syscall.Syscall(_glDrawArrays.Addr(), 3, uintptr(mode), uintptr(first), uintptr(count)) syscall.Syscall(_glDrawArrays.Addr(), 3, uintptr(mode), uintptr(first), uintptr(count))
} }
func (c *Functions) DrawElements(mode Enum, count int, ty Enum, offset int) { func (c *Functions) DrawElements(mode Enum, count int, ty Enum, offset int) {
syscall.Syscall6(_glDrawElements.Addr(), 4, uintptr(mode), uintptr(count), uintptr(ty), uintptr(offset), 0, 0) syscall.Syscall6(_glDrawElements.Addr(), 4, uintptr(mode), uintptr(count), uintptr(ty), uintptr(offset), 0, 0)
} }
func (f *Functions) DispatchCompute(x, y, z int) { func (f *Functions) DispatchCompute(x, y, z int) {
panic("not implemented") panic("not implemented")
} }
func (c *Functions) Enable(cap Enum) { func (c *Functions) Enable(cap Enum) {
syscall.Syscall(_glEnable.Addr(), 1, uintptr(cap), 0, 0) syscall.Syscall(_glEnable.Addr(), 1, uintptr(cap), 0, 0)
} }
func (c *Functions) EnableVertexAttribArray(a Attrib) { func (c *Functions) EnableVertexAttribArray(a Attrib) {
syscall.Syscall(_glEnableVertexAttribArray.Addr(), 1, uintptr(a), 0, 0) syscall.Syscall(_glEnableVertexAttribArray.Addr(), 1, uintptr(a), 0, 0)
} }
func (f *Functions) EndQuery(target Enum) { func (f *Functions) EndQuery(target Enum) {
syscall.Syscall(_glEndQuery.Addr(), 1, uintptr(target), 0, 0) syscall.Syscall(_glEndQuery.Addr(), 1, uintptr(target), 0, 0)
} }
func (c *Functions) Finish() { func (c *Functions) Finish() {
syscall.Syscall(_glFinish.Addr(), 0, 0, 0, 0) syscall.Syscall(_glFinish.Addr(), 0, 0, 0, 0)
} }
func (c *Functions) Flush() { func (c *Functions) Flush() {
syscall.Syscall(_glFlush.Addr(), 0, 0, 0, 0) syscall.Syscall(_glFlush.Addr(), 0, 0, 0, 0)
} }
func (c *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarget Enum, renderbuffer Renderbuffer) { func (c *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarget Enum, renderbuffer Renderbuffer) {
syscall.Syscall6(_glFramebufferRenderbuffer.Addr(), 4, uintptr(target), uintptr(attachment), uintptr(renderbuffertarget), uintptr(renderbuffer.V), 0, 0) syscall.Syscall6(_glFramebufferRenderbuffer.Addr(), 4, uintptr(target), uintptr(attachment), uintptr(renderbuffertarget), uintptr(renderbuffer.V), 0, 0)
} }
func (c *Functions) FramebufferTexture2D(target, attachment, texTarget Enum, t Texture, level int) { func (c *Functions) FramebufferTexture2D(target, attachment, texTarget Enum, t Texture, level int) {
syscall.Syscall6(_glFramebufferTexture2D.Addr(), 5, uintptr(target), uintptr(attachment), uintptr(texTarget), uintptr(t.V), uintptr(level), 0) syscall.Syscall6(_glFramebufferTexture2D.Addr(), 5, uintptr(target), uintptr(attachment), uintptr(texTarget), uintptr(t.V), uintptr(level), 0)
} }
func (f *Functions) GetUniformBlockIndex(p Program, name string) uint { func (f *Functions) GetUniformBlockIndex(p Program, name string) uint {
cname := cString(name) cname := cString(name)
c0 := &cname[0] c0 := &cname[0]
@@ -481,30 +311,24 @@ func (f *Functions) GetUniformBlockIndex(p Program, name string) uint {
issue34474KeepAlive(c0) issue34474KeepAlive(c0)
return uint(u) return uint(u)
} }
func (c *Functions) GetBinding(pname Enum) Object { func (c *Functions) GetBinding(pname Enum) Object {
return Object{uint(c.GetInteger(pname))} return Object{uint(c.GetInteger(pname))}
} }
func (c *Functions) GetBindingi(pname Enum, idx int) Object { func (c *Functions) GetBindingi(pname Enum, idx int) Object {
return Object{uint(c.GetIntegeri(pname, idx))} return Object{uint(c.GetIntegeri(pname, idx))}
} }
func (c *Functions) GetError() Enum { func (c *Functions) GetError() Enum {
e, _, _ := syscall.Syscall(_glGetError.Addr(), 0, 0, 0, 0) e, _, _ := syscall.Syscall(_glGetError.Addr(), 0, 0, 0, 0)
return Enum(e) return Enum(e)
} }
func (c *Functions) GetRenderbufferParameteri(target, pname Enum) int { func (c *Functions) GetRenderbufferParameteri(target, pname Enum) int {
syscall.Syscall(_glGetRenderbufferParameteriv.Addr(), 3, uintptr(target), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0]))) syscall.Syscall(_glGetRenderbufferParameteriv.Addr(), 3, uintptr(target), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])))
return int(c.int32s[0]) return int(c.int32s[0])
} }
func (c *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname Enum) int { func (c *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname Enum) int {
syscall.Syscall6(_glGetFramebufferAttachmentParameteriv.Addr(), 4, uintptr(target), uintptr(attachment), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0, 0) syscall.Syscall6(_glGetFramebufferAttachmentParameteriv.Addr(), 4, uintptr(target), uintptr(attachment), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0, 0)
return int(c.int32s[0]) return int(c.int32s[0])
} }
func (c *Functions) GetInteger4(pname Enum) [4]int { func (c *Functions) GetInteger4(pname Enum) [4]int {
syscall.Syscall(_glGetIntegerv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0) syscall.Syscall(_glGetIntegerv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0)
var r [4]int var r [4]int
@@ -513,34 +337,28 @@ func (c *Functions) GetInteger4(pname Enum) [4]int {
} }
return r return r
} }
func (c *Functions) GetInteger(pname Enum) int { func (c *Functions) GetInteger(pname Enum) int {
syscall.Syscall(_glGetIntegerv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0) syscall.Syscall(_glGetIntegerv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0)
return int(c.int32s[0]) return int(c.int32s[0])
} }
func (c *Functions) GetIntegeri(pname Enum, idx int) int { func (c *Functions) GetIntegeri(pname Enum, idx int) int {
syscall.Syscall(_glGetIntegeri_v.Addr(), 3, uintptr(pname), uintptr(idx), uintptr(unsafe.Pointer(&c.int32s[0]))) syscall.Syscall(_glGetIntegeri_v.Addr(), 3, uintptr(pname), uintptr(idx), uintptr(unsafe.Pointer(&c.int32s[0])))
return int(c.int32s[0]) return int(c.int32s[0])
} }
func (c *Functions) GetFloat(pname Enum) float32 { func (c *Functions) GetFloat(pname Enum) float32 {
syscall.Syscall(_glGetFloatv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.float32s[0])), 0) syscall.Syscall(_glGetFloatv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.float32s[0])), 0)
return c.float32s[0] return c.float32s[0]
} }
func (c *Functions) GetFloat4(pname Enum) [4]float32 { func (c *Functions) GetFloat4(pname Enum) [4]float32 {
syscall.Syscall(_glGetFloatv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.float32s[0])), 0) syscall.Syscall(_glGetFloatv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.float32s[0])), 0)
var r [4]float32 var r [4]float32
copy(r[:], c.float32s[:]) copy(r[:], c.float32s[:])
return r return r
} }
func (c *Functions) GetProgrami(p Program, pname Enum) int { func (c *Functions) GetProgrami(p Program, pname Enum) int {
syscall.Syscall(_glGetProgramiv.Addr(), 3, uintptr(p.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0]))) syscall.Syscall(_glGetProgramiv.Addr(), 3, uintptr(p.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])))
return int(c.int32s[0]) return int(c.int32s[0])
} }
func (c *Functions) GetProgramInfoLog(p Program) string { func (c *Functions) GetProgramInfoLog(p Program) string {
n := c.GetProgrami(p, INFO_LOG_LENGTH) n := c.GetProgrami(p, INFO_LOG_LENGTH)
if n == 0 { if n == 0 {
@@ -550,29 +368,24 @@ func (c *Functions) GetProgramInfoLog(p Program) string {
syscall.Syscall6(_glGetProgramInfoLog.Addr(), 4, uintptr(p.V), uintptr(len(buf)), 0, uintptr(unsafe.Pointer(&buf[0])), 0, 0) syscall.Syscall6(_glGetProgramInfoLog.Addr(), 4, uintptr(p.V), uintptr(len(buf)), 0, uintptr(unsafe.Pointer(&buf[0])), 0, 0)
return string(buf) return string(buf)
} }
func (c *Functions) GetQueryObjectuiv(query Query, pname Enum) uint { func (c *Functions) GetQueryObjectuiv(query Query, pname Enum) uint {
syscall.Syscall(_glGetQueryObjectuiv.Addr(), 3, uintptr(query.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0]))) syscall.Syscall(_glGetQueryObjectuiv.Addr(), 3, uintptr(query.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])))
return uint(c.int32s[0]) return uint(c.int32s[0])
} }
func (c *Functions) GetShaderi(s Shader, pname Enum) int { func (c *Functions) GetShaderi(s Shader, pname Enum) int {
syscall.Syscall(_glGetShaderiv.Addr(), 3, uintptr(s.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0]))) syscall.Syscall(_glGetShaderiv.Addr(), 3, uintptr(s.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])))
return int(c.int32s[0]) return int(c.int32s[0])
} }
func (c *Functions) GetShaderInfoLog(s Shader) string { func (c *Functions) GetShaderInfoLog(s Shader) string {
n := c.GetShaderi(s, INFO_LOG_LENGTH) n := c.GetShaderi(s, INFO_LOG_LENGTH)
buf := make([]byte, n) buf := make([]byte, n)
syscall.Syscall6(_glGetShaderInfoLog.Addr(), 4, uintptr(s.V), uintptr(len(buf)), 0, uintptr(unsafe.Pointer(&buf[0])), 0, 0) syscall.Syscall6(_glGetShaderInfoLog.Addr(), 4, uintptr(s.V), uintptr(len(buf)), 0, uintptr(unsafe.Pointer(&buf[0])), 0, 0)
return string(buf) return string(buf)
} }
func (c *Functions) GetString(pname Enum) string { func (c *Functions) GetString(pname Enum) string {
s, _, _ := syscall.Syscall(_glGetString.Addr(), 1, uintptr(pname), 0, 0) s, _, _ := syscall.Syscall(_glGetString.Addr(), 1, uintptr(pname), 0, 0)
return windows.BytePtrToString((*byte)(unsafe.Pointer(s))) return windows.BytePtrToString((*byte)(unsafe.Pointer(s)))
} }
func (c *Functions) GetUniformLocation(p Program, name string) Uniform { func (c *Functions) GetUniformLocation(p Program, name string) Uniform {
cname := cString(name) cname := cString(name)
c0 := &cname[0] c0 := &cname[0]
@@ -580,7 +393,6 @@ func (c *Functions) GetUniformLocation(p Program, name string) Uniform {
issue34474KeepAlive(c0) issue34474KeepAlive(c0)
return Uniform{int(u)} return Uniform{int(u)}
} }
func (c *Functions) GetVertexAttrib(index int, pname Enum) int { func (c *Functions) GetVertexAttrib(index int, pname Enum) int {
syscall.Syscall(_glGetVertexAttribiv.Addr(), 3, uintptr(index), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0]))) syscall.Syscall(_glGetVertexAttribiv.Addr(), 3, uintptr(index), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])))
return int(c.int32s[0]) return int(c.int32s[0])
@@ -594,7 +406,6 @@ func (c *Functions) GetVertexAttribPointer(index int, pname Enum) uintptr {
syscall.Syscall(_glGetVertexAttribPointerv.Addr(), 3, uintptr(index), uintptr(pname), uintptr(unsafe.Pointer(&c.uintptrs[0]))) syscall.Syscall(_glGetVertexAttribPointerv.Addr(), 3, uintptr(index), uintptr(pname), uintptr(unsafe.Pointer(&c.uintptrs[0])))
return c.uintptrs[0] return c.uintptrs[0]
} }
func (c *Functions) InvalidateFramebuffer(target, attachment Enum) { func (c *Functions) InvalidateFramebuffer(target, attachment Enum) {
addr := _glInvalidateFramebuffer.Addr() addr := _glInvalidateFramebuffer.Addr()
if addr == 0 { if addr == 0 {
@@ -603,99 +414,77 @@ func (c *Functions) InvalidateFramebuffer(target, attachment Enum) {
} }
syscall.Syscall(addr, 3, uintptr(target), 1, uintptr(unsafe.Pointer(&attachment))) syscall.Syscall(addr, 3, uintptr(target), 1, uintptr(unsafe.Pointer(&attachment)))
} }
func (f *Functions) IsEnabled(cap Enum) bool { func (f *Functions) IsEnabled(cap Enum) bool {
u, _, _ := syscall.Syscall(_glIsEnabled.Addr(), 1, uintptr(cap), 0, 0) u, _, _ := syscall.Syscall(_glIsEnabled.Addr(), 1, uintptr(cap), 0, 0)
return u == TRUE return u == TRUE
} }
func (c *Functions) LinkProgram(p Program) { func (c *Functions) LinkProgram(p Program) {
syscall.Syscall(_glLinkProgram.Addr(), 1, uintptr(p.V), 0, 0) syscall.Syscall(_glLinkProgram.Addr(), 1, uintptr(p.V), 0, 0)
} }
func (c *Functions) PixelStorei(pname Enum, param int) { func (c *Functions) PixelStorei(pname Enum, param int) {
syscall.Syscall(_glPixelStorei.Addr(), 2, uintptr(pname), uintptr(param), 0) syscall.Syscall(_glPixelStorei.Addr(), 2, uintptr(pname), uintptr(param), 0)
} }
func (f *Functions) MemoryBarrier(barriers Enum) { func (f *Functions) MemoryBarrier(barriers Enum) {
panic("not implemented") panic("not implemented")
} }
func (f *Functions) MapBufferRange(target Enum, offset, length int, access Enum) []byte { func (f *Functions) MapBufferRange(target Enum, offset, length int, access Enum) []byte {
panic("not implemented") panic("not implemented")
} }
func (f *Functions) ReadPixels(x, y, width, height int, format, ty Enum, data []byte) { func (f *Functions) ReadPixels(x, y, width, height int, format, ty Enum, data []byte) {
d0 := &data[0] d0 := &data[0]
syscall.Syscall9(_glReadPixels.Addr(), 7, uintptr(x), uintptr(y), uintptr(width), uintptr(height), uintptr(format), uintptr(ty), uintptr(unsafe.Pointer(d0)), 0, 0) syscall.Syscall9(_glReadPixels.Addr(), 7, uintptr(x), uintptr(y), uintptr(width), uintptr(height), uintptr(format), uintptr(ty), uintptr(unsafe.Pointer(d0)), 0, 0)
issue34474KeepAlive(d0) issue34474KeepAlive(d0)
} }
func (c *Functions) RenderbufferStorage(target, internalformat Enum, width, height int) { func (c *Functions) RenderbufferStorage(target, internalformat Enum, width, height int) {
syscall.Syscall6(_glRenderbufferStorage.Addr(), 4, uintptr(target), uintptr(internalformat), uintptr(width), uintptr(height), 0, 0) syscall.Syscall6(_glRenderbufferStorage.Addr(), 4, uintptr(target), uintptr(internalformat), uintptr(width), uintptr(height), 0, 0)
} }
func (c *Functions) Scissor(x, y, width, height int32) { func (c *Functions) Scissor(x, y, width, height int32) {
syscall.Syscall6(_glScissor.Addr(), 4, uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0, 0) syscall.Syscall6(_glScissor.Addr(), 4, uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0, 0)
} }
func (c *Functions) ShaderSource(s Shader, src string) { func (c *Functions) ShaderSource(s Shader, src string) {
var n uintptr = uintptr(len(src)) var n uintptr = uintptr(len(src))
psrc := &src psrc := &src
syscall.Syscall6(_glShaderSource.Addr(), 4, uintptr(s.V), 1, uintptr(unsafe.Pointer(psrc)), uintptr(unsafe.Pointer(&n)), 0, 0) syscall.Syscall6(_glShaderSource.Addr(), 4, uintptr(s.V), 1, uintptr(unsafe.Pointer(psrc)), uintptr(unsafe.Pointer(&n)), 0, 0)
issue34474KeepAlive(psrc) issue34474KeepAlive(psrc)
} }
func (f *Functions) TexImage2D(target Enum, level int, internalFormat Enum, width int, height int, format Enum, ty Enum) { func (f *Functions) TexImage2D(target Enum, level int, internalFormat Enum, width int, height int, format Enum, ty Enum) {
syscall.Syscall9(_glTexImage2D.Addr(), 9, uintptr(target), uintptr(level), uintptr(internalFormat), uintptr(width), uintptr(height), 0, uintptr(format), uintptr(ty), 0) syscall.Syscall9(_glTexImage2D.Addr(), 9, uintptr(target), uintptr(level), uintptr(internalFormat), uintptr(width), uintptr(height), 0, uintptr(format), uintptr(ty), 0)
} }
func (f *Functions) TexStorage2D(target Enum, levels int, internalFormat Enum, width, height int) { func (f *Functions) TexStorage2D(target Enum, levels int, internalFormat Enum, width, height int) {
syscall.Syscall6(_glTexStorage2D.Addr(), 5, uintptr(target), uintptr(levels), uintptr(internalFormat), uintptr(width), uintptr(height), 0) syscall.Syscall6(_glTexStorage2D.Addr(), 5, uintptr(target), uintptr(levels), uintptr(internalFormat), uintptr(width), uintptr(height), 0)
} }
func (c *Functions) TexSubImage2D(target Enum, level int, x, y, width, height int, format, ty Enum, data []byte) { func (c *Functions) TexSubImage2D(target Enum, level int, x, y, width, height int, format, ty Enum, data []byte) {
d0 := &data[0] d0 := &data[0]
syscall.Syscall9(_glTexSubImage2D.Addr(), 9, uintptr(target), uintptr(level), uintptr(x), uintptr(y), uintptr(width), uintptr(height), uintptr(format), uintptr(ty), uintptr(unsafe.Pointer(d0))) syscall.Syscall9(_glTexSubImage2D.Addr(), 9, uintptr(target), uintptr(level), uintptr(x), uintptr(y), uintptr(width), uintptr(height), uintptr(format), uintptr(ty), uintptr(unsafe.Pointer(d0)))
issue34474KeepAlive(d0) issue34474KeepAlive(d0)
} }
func (c *Functions) TexParameteri(target, pname Enum, param int) { func (c *Functions) TexParameteri(target, pname Enum, param int) {
syscall.Syscall(_glTexParameteri.Addr(), 3, uintptr(target), uintptr(pname), uintptr(param)) syscall.Syscall(_glTexParameteri.Addr(), 3, uintptr(target), uintptr(pname), uintptr(param))
} }
func (f *Functions) UniformBlockBinding(p Program, uniformBlockIndex uint, uniformBlockBinding uint) { func (f *Functions) UniformBlockBinding(p Program, uniformBlockIndex uint, uniformBlockBinding uint) {
syscall.Syscall(_glUniformBlockBinding.Addr(), 3, uintptr(p.V), uintptr(uniformBlockIndex), uintptr(uniformBlockBinding)) syscall.Syscall(_glUniformBlockBinding.Addr(), 3, uintptr(p.V), uintptr(uniformBlockIndex), uintptr(uniformBlockBinding))
} }
func (c *Functions) Uniform1f(dst Uniform, v float32) { func (c *Functions) Uniform1f(dst Uniform, v float32) {
syscall.Syscall(_glUniform1f.Addr(), 2, uintptr(dst.V), uintptr(math.Float32bits(v)), 0) syscall.Syscall(_glUniform1f.Addr(), 2, uintptr(dst.V), uintptr(math.Float32bits(v)), 0)
} }
func (c *Functions) Uniform1i(dst Uniform, v int) { func (c *Functions) Uniform1i(dst Uniform, v int) {
syscall.Syscall(_glUniform1i.Addr(), 2, uintptr(dst.V), uintptr(v), 0) syscall.Syscall(_glUniform1i.Addr(), 2, uintptr(dst.V), uintptr(v), 0)
} }
func (c *Functions) Uniform2f(dst Uniform, v0, v1 float32) { func (c *Functions) Uniform2f(dst Uniform, v0, v1 float32) {
syscall.Syscall(_glUniform2f.Addr(), 3, uintptr(dst.V), uintptr(math.Float32bits(v0)), uintptr(math.Float32bits(v1))) syscall.Syscall(_glUniform2f.Addr(), 3, uintptr(dst.V), uintptr(math.Float32bits(v0)), uintptr(math.Float32bits(v1)))
} }
func (c *Functions) Uniform3f(dst Uniform, v0, v1, v2 float32) { func (c *Functions) Uniform3f(dst Uniform, v0, v1, v2 float32) {
syscall.Syscall6(_glUniform3f.Addr(), 4, uintptr(dst.V), uintptr(math.Float32bits(v0)), uintptr(math.Float32bits(v1)), uintptr(math.Float32bits(v2)), 0, 0) syscall.Syscall6(_glUniform3f.Addr(), 4, uintptr(dst.V), uintptr(math.Float32bits(v0)), uintptr(math.Float32bits(v1)), uintptr(math.Float32bits(v2)), 0, 0)
} }
func (c *Functions) Uniform4f(dst Uniform, v0, v1, v2, v3 float32) { func (c *Functions) Uniform4f(dst Uniform, v0, v1, v2, v3 float32) {
syscall.Syscall6(_glUniform4f.Addr(), 5, uintptr(dst.V), uintptr(math.Float32bits(v0)), uintptr(math.Float32bits(v1)), uintptr(math.Float32bits(v2)), uintptr(math.Float32bits(v3)), 0) syscall.Syscall6(_glUniform4f.Addr(), 5, uintptr(dst.V), uintptr(math.Float32bits(v0)), uintptr(math.Float32bits(v1)), uintptr(math.Float32bits(v2)), uintptr(math.Float32bits(v3)), 0)
} }
func (c *Functions) UseProgram(p Program) { func (c *Functions) UseProgram(p Program) {
syscall.Syscall(_glUseProgram.Addr(), 1, uintptr(p.V), 0, 0) syscall.Syscall(_glUseProgram.Addr(), 1, uintptr(p.V), 0, 0)
} }
func (f *Functions) UnmapBuffer(target Enum) bool { func (f *Functions) UnmapBuffer(target Enum) bool {
panic("not implemented") panic("not implemented")
} }
func (c *Functions) VertexAttribPointer(dst Attrib, size int, ty Enum, normalized bool, stride, offset int) { func (c *Functions) VertexAttribPointer(dst Attrib, size int, ty Enum, normalized bool, stride, offset int) {
var norm uintptr var norm uintptr
if normalized { if normalized {
@@ -703,7 +492,6 @@ func (c *Functions) VertexAttribPointer(dst Attrib, size int, ty Enum, normalize
} }
syscall.Syscall6(_glVertexAttribPointer.Addr(), 6, uintptr(dst), uintptr(size), uintptr(ty), norm, uintptr(stride), uintptr(offset)) syscall.Syscall6(_glVertexAttribPointer.Addr(), 6, uintptr(dst), uintptr(size), uintptr(ty), norm, uintptr(stride), uintptr(offset))
} }
func (c *Functions) Viewport(x, y, width, height int) { func (c *Functions) Viewport(x, y, width, height int) {
syscall.Syscall6(_glViewport.Addr(), 4, uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0, 0) syscall.Syscall6(_glViewport.Addr(), 4, uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0, 0)
} }
@@ -716,6 +504,6 @@ func cString(s string) []byte {
// issue34474KeepAlive calls runtime.KeepAlive as a // issue34474KeepAlive calls runtime.KeepAlive as a
// workaround for golang.org/issue/34474. // workaround for golang.org/issue/34474.
func issue34474KeepAlive(v any) { func issue34474KeepAlive(v interface{}) {
runtime.KeepAlive(v) runtime.KeepAlive(v)
} }
+5 -5
View File
@@ -18,7 +18,7 @@ type Ops struct {
// data contains the serialized operations. // data contains the serialized operations.
data []byte data []byte
// refs hold external references for operations. // refs hold external references for operations.
refs []any refs []interface{}
// stringRefs provides space for string references, pointers to which will // stringRefs provides space for string references, pointers to which will
// be stored in refs. Storing a string directly in refs would cause a heap // be stored in refs. Storing a string directly in refs would cause a heap
// allocation, to store the string header in an interface value. The backing // allocation, to store the string header in an interface value. The backing
@@ -256,7 +256,7 @@ func PopOp(o *Ops, kind StackKind, sid StackID, macroID uint32) {
o.stacks[kind].pop(sid) o.stacks[kind].pop(sid)
} }
func Write1(o *Ops, n int, ref1 any) []byte { func Write1(o *Ops, n int, ref1 interface{}) []byte {
o.data = append(o.data, make([]byte, n)...) o.data = append(o.data, make([]byte, n)...)
o.refs = append(o.refs, ref1) o.refs = append(o.refs, ref1)
return o.data[len(o.data)-n:] return o.data[len(o.data)-n:]
@@ -269,20 +269,20 @@ func Write1String(o *Ops, n int, ref1 string) []byte {
return o.data[len(o.data)-n:] return o.data[len(o.data)-n:]
} }
func Write2(o *Ops, n int, ref1, ref2 any) []byte { func Write2(o *Ops, n int, ref1, ref2 interface{}) []byte {
o.data = append(o.data, make([]byte, n)...) o.data = append(o.data, make([]byte, n)...)
o.refs = append(o.refs, ref1, ref2) o.refs = append(o.refs, ref1, ref2)
return o.data[len(o.data)-n:] return o.data[len(o.data)-n:]
} }
func Write2String(o *Ops, n int, ref1 any, ref2 string) []byte { func Write2String(o *Ops, n int, ref1 interface{}, ref2 string) []byte {
o.data = append(o.data, make([]byte, n)...) o.data = append(o.data, make([]byte, n)...)
o.stringRefs = append(o.stringRefs, ref2) o.stringRefs = append(o.stringRefs, ref2)
o.refs = append(o.refs, ref1, &o.stringRefs[len(o.stringRefs)-1]) o.refs = append(o.refs, ref1, &o.stringRefs[len(o.stringRefs)-1])
return o.data[len(o.data)-n:] return o.data[len(o.data)-n:]
} }
func Write3(o *Ops, n int, ref1, ref2, ref3 any) []byte { func Write3(o *Ops, n int, ref1, ref2, ref3 interface{}) []byte {
o.data = append(o.data, make([]byte, n)...) o.data = append(o.data, make([]byte, n)...)
o.refs = append(o.refs, ref1, ref2, ref3) o.refs = append(o.refs, ref1, ref2, ref3)
return o.data[len(o.data)-n:] return o.data[len(o.data)-n:]
+2 -2
View File
@@ -20,7 +20,7 @@ type Reader struct {
type EncodedOp struct { type EncodedOp struct {
Key Key Key Key
Data []byte Data []byte
Refs []any Refs []interface{}
} }
// Key is a unique key for a given op. // Key is a unique key for a given op.
@@ -175,7 +175,7 @@ func (op *opMacroDef) decode(data []byte) {
op.endpc.refs = bo.Uint32(data[5:]) op.endpc.refs = bo.Uint32(data[5:])
} }
func (m *macroOp) decode(data []byte, refs []any) { func (m *macroOp) decode(data []byte, refs []interface{}) {
if len(data) < TypeCallLen || len(refs) < 1 || OpType(data[0]) != TypeCall { if len(data) < TypeCallLen || len(refs) < 1 || OpType(data[0]) != TypeCall {
panic("invalid op") panic("invalid op")
} }
+12 -21
View File
@@ -87,7 +87,7 @@ func (qs *StrokeQuads) lineTo(pt f32.Point) {
func (qs *StrokeQuads) arc(f1, f2 f32.Point, angle float32) { func (qs *StrokeQuads) arc(f1, f2 f32.Point, angle float32) {
pen := qs.pen() pen := qs.pen()
m, segments := ArcTransform(pen, f1.Add(pen), f2.Add(pen), angle) m, segments := ArcTransform(pen, f1.Add(pen), f2.Add(pen), angle)
for range segments { for i := 0; i < segments; i++ {
p0 := qs.pen() p0 := qs.pen()
p1 := m.Transform(p0) p1 := m.Transform(p0)
p2 := m.Transform(p1) p2 := m.Transform(p1)
@@ -327,26 +327,13 @@ func strokePathNorm(p0, p1, p2 f32.Point, t, d float32) f32.Point {
func rot90CW(p f32.Point) f32.Point { return f32.Pt(+p.Y, -p.X) } func rot90CW(p f32.Point) f32.Point { return f32.Pt(+p.Y, -p.X) }
func normPt(p f32.Point, l float32) f32.Point { func normPt(p f32.Point, l float32) f32.Point {
if (p.X == 0 && p.Y == 0) || l == 0 { if (p.X == 0 && p.Y == l) || (p.Y == 0 && p.X == l) {
return f32.Point{} return f32.Point{X: p.X, Y: p.Y}
}
isVerticalUnit := p.X == 0 && (p.Y == l || p.Y == -l)
isHorizontalUnit := p.Y == 0 && (p.X == l || p.X == -l)
if isVerticalUnit || isHorizontalUnit {
if math.Signbit(float64(l)) {
return f32.Point{X: -p.X, Y: -p.Y}
} else {
return f32.Point{X: p.X, Y: p.Y}
}
} }
d := math.Hypot(float64(p.X), float64(p.Y)) d := math.Hypot(float64(p.X), float64(p.Y))
l64 := float64(l) l64 := float64(l)
if math.Abs(d-l64) < 1e-10 { if math.Abs(d-l64) < 1e-10 {
if math.Signbit(float64(l)) { return f32.Point{}
return f32.Point{X: -p.X, Y: -p.Y}
} else {
return f32.Point{X: p.X, Y: p.Y}
}
} }
n := float32(l64 / d) n := float32(l64 / d)
return f32.Point{X: p.X * n, Y: p.Y * n} return f32.Point{X: p.X * n, Y: p.Y * n}
@@ -453,6 +440,7 @@ func flattenQuadBezier(qs StrokeQuads, p0, p1, p2 f32.Point, d, flatness float32
} }
func (qs *StrokeQuads) addLine(p0, ctrl, p1 f32.Point, t, d float32) { func (qs *StrokeQuads) addLine(p0, ctrl, p1 f32.Point, t, d float32) {
switch i := len(*qs); i { switch i := len(*qs); i {
case 0: case 0:
p0 = p0.Add(strokePathNorm(p0, ctrl, p1, 0, d)) p0 = p0.Add(strokePathNorm(p0, ctrl, p1, 0, d))
@@ -485,6 +473,7 @@ func quadInterp(p, q f32.Point, t float32) f32.Point {
// quadBezierSplit returns the pair of triplets (from,ctrl,to) Bézier curve, // quadBezierSplit returns the pair of triplets (from,ctrl,to) Bézier curve,
// split before (resp. after) the provided parametric t value. // split before (resp. after) the provided parametric t value.
func quadBezierSplit(p0, p1, p2 f32.Point, t float32) (f32.Point, f32.Point, f32.Point, f32.Point, f32.Point, f32.Point) { func quadBezierSplit(p0, p1, p2 f32.Point, t float32) (f32.Point, f32.Point, f32.Point, f32.Point, f32.Point, f32.Point) {
var ( var (
b0 = p0 b0 = p0
b1 = quadInterp(p0, p1, t) b1 = quadInterp(p0, p1, t)
@@ -588,10 +577,12 @@ func ArcTransform(p, f1, f2 f32.Point, angle float32) (transform f32.Affine2D, s
} }
} }
θ := angle / float32(segments) var (
ref := f32.AffineId() // transform from absolute frame to ellipse-based one θ = angle / float32(segments)
rot := f32.AffineId() // rotation matrix for each segment ref f32.Affine2D // transform from absolute frame to ellipse-based one
inv := f32.AffineId() // transform from ellipse-based frame to absolute one rot f32.Affine2D // rotation matrix for each segment
inv f32.Affine2D // transform from ellipse-based frame to absolute one
)
center := f32.Point{ center := f32.Point{
X: 0.5 * (f1.X + f2.X), X: 0.5 * (f1.X + f2.X),
Y: 0.5 * (f1.Y + f2.Y), Y: 0.5 * (f1.Y + f2.Y),
+1 -106
View File
@@ -9,111 +9,6 @@ import (
"gioui.org/internal/f32" "gioui.org/internal/f32"
) )
func TestNormPt(t *testing.T) {
type scenario struct {
l float32
ptIn, ptOut f32.Point
}
scenarios := []scenario{
// l!=0 && X=Y=0
{l: 10, ptIn: f32.Point{X: 0, Y: 0}, ptOut: f32.Point{X: 0, Y: 0}},
{l: -10, ptIn: f32.Point{X: 0, Y: 0}, ptOut: f32.Point{X: 0, Y: 0}},
// l>0 & X
{l: +20, ptIn: f32.Point{X: +30, Y: 0}, ptOut: f32.Point{X: +20, Y: 0}},
{l: +20, ptIn: f32.Point{X: +20, Y: 0}, ptOut: f32.Point{X: +20, Y: 0}},
{l: +20, ptIn: f32.Point{X: +10, Y: 0}, ptOut: f32.Point{X: +20, Y: 0}},
{l: +20, ptIn: f32.Point{X: -10, Y: 0}, ptOut: f32.Point{X: -20, Y: 0}},
{l: +20, ptIn: f32.Point{X: -20, Y: 0}, ptOut: f32.Point{X: -20, Y: 0}},
{l: +20, ptIn: f32.Point{X: -30, Y: 0}, ptOut: f32.Point{X: -20, Y: 0}},
// l<0 & X
{l: -20, ptIn: f32.Point{X: +30, Y: 0}, ptOut: f32.Point{X: -20, Y: 0}},
{l: -20, ptIn: f32.Point{X: +20, Y: 0}, ptOut: f32.Point{X: -20, Y: 0}},
{l: -20, ptIn: f32.Point{X: +10, Y: 0}, ptOut: f32.Point{X: -20, Y: 0}},
{l: -20, ptIn: f32.Point{X: -10, Y: 0}, ptOut: f32.Point{X: +20, Y: 0}},
{l: -20, ptIn: f32.Point{X: -20, Y: 0}, ptOut: f32.Point{X: +20, Y: 0}},
{l: -20, ptIn: f32.Point{X: -30, Y: 0}, ptOut: f32.Point{X: +20, Y: 0}},
// l>0 & Y
{l: +20, ptIn: f32.Point{X: 0, Y: +30}, ptOut: f32.Point{X: 0, Y: +20}},
{l: +20, ptIn: f32.Point{X: 0, Y: +20}, ptOut: f32.Point{X: 0, Y: +20}},
{l: +20, ptIn: f32.Point{X: 0, Y: +10}, ptOut: f32.Point{X: 0, Y: +20}},
{l: +20, ptIn: f32.Point{X: 0, Y: -10}, ptOut: f32.Point{X: 0, Y: -20}},
{l: +20, ptIn: f32.Point{X: 0, Y: -20}, ptOut: f32.Point{X: 0, Y: -20}},
{l: +20, ptIn: f32.Point{X: 0, Y: -30}, ptOut: f32.Point{X: 0, Y: -20}},
// l<0 & Y
{l: -20, ptIn: f32.Point{X: 0, Y: +30}, ptOut: f32.Point{X: 0, Y: -20}},
{l: -20, ptIn: f32.Point{X: 0, Y: +20}, ptOut: f32.Point{X: 0, Y: -20}},
{l: -20, ptIn: f32.Point{X: 0, Y: +10}, ptOut: f32.Point{X: 0, Y: -20}},
{l: -20, ptIn: f32.Point{X: 0, Y: -10}, ptOut: f32.Point{X: 0, Y: +20}},
{l: -20, ptIn: f32.Point{X: 0, Y: -20}, ptOut: f32.Point{X: 0, Y: +20}},
{l: -20, ptIn: f32.Point{X: 0, Y: -30}, ptOut: f32.Point{X: 0, Y: +20}},
// l>0 && X=Y
{l: +20, ptIn: f32.Point{X: +90, Y: +90}, ptOut: f32.Point{X: +14.142137, Y: +14.142137}},
{l: +20, ptIn: f32.Point{X: +30, Y: +30}, ptOut: f32.Point{X: +14.142136, Y: +14.142136}},
{l: +20, ptIn: f32.Point{X: +20, Y: +20}, ptOut: f32.Point{X: +14.142136, Y: +14.142136}},
{l: +20, ptIn: f32.Point{X: +10, Y: +10}, ptOut: f32.Point{X: +14.142136, Y: +14.142136}},
{l: +20, ptIn: f32.Point{X: -10, Y: -10}, ptOut: f32.Point{X: -14.142136, Y: -14.142136}},
{l: +20, ptIn: f32.Point{X: -20, Y: -20}, ptOut: f32.Point{X: -14.142136, Y: -14.142136}},
{l: +20, ptIn: f32.Point{X: -30, Y: -30}, ptOut: f32.Point{X: -14.142136, Y: -14.142136}},
{l: +20, ptIn: f32.Point{X: -90, Y: -90}, ptOut: f32.Point{X: -14.142137, Y: -14.142137}},
// l>0 && X=-Y
{l: +20, ptIn: f32.Point{X: +90, Y: -90}, ptOut: f32.Point{X: +14.142137, Y: -14.142137}},
{l: +20, ptIn: f32.Point{X: +30, Y: -30}, ptOut: f32.Point{X: +14.142136, Y: -14.142136}},
{l: +20, ptIn: f32.Point{X: +20, Y: -20}, ptOut: f32.Point{X: +14.142136, Y: -14.142136}},
{l: +20, ptIn: f32.Point{X: +10, Y: -10}, ptOut: f32.Point{X: +14.142136, Y: -14.142136}},
{l: +20, ptIn: f32.Point{X: -10, Y: +10}, ptOut: f32.Point{X: -14.142136, Y: +14.142136}},
{l: +20, ptIn: f32.Point{X: -20, Y: +20}, ptOut: f32.Point{X: -14.142136, Y: +14.142136}},
{l: +20, ptIn: f32.Point{X: -30, Y: +30}, ptOut: f32.Point{X: -14.142136, Y: +14.142136}},
{l: +20, ptIn: f32.Point{X: -90, Y: +90}, ptOut: f32.Point{X: -14.142137, Y: +14.142137}},
// l<0 && X=Y
{l: -20, ptIn: f32.Point{X: +90, Y: +90}, ptOut: f32.Point{X: -14.142137, Y: -14.142137}},
{l: -20, ptIn: f32.Point{X: +30, Y: +30}, ptOut: f32.Point{X: -14.142136, Y: -14.142136}},
{l: -20, ptIn: f32.Point{X: +20, Y: +20}, ptOut: f32.Point{X: -14.142136, Y: -14.142136}},
{l: -20, ptIn: f32.Point{X: +10, Y: +10}, ptOut: f32.Point{X: -14.142136, Y: -14.142136}},
{l: -20, ptIn: f32.Point{X: -10, Y: -10}, ptOut: f32.Point{X: +14.142136, Y: +14.142136}},
{l: -20, ptIn: f32.Point{X: -20, Y: -20}, ptOut: f32.Point{X: +14.142136, Y: +14.142136}},
{l: -20, ptIn: f32.Point{X: -30, Y: -30}, ptOut: f32.Point{X: +14.142136, Y: +14.142136}},
{l: -20, ptIn: f32.Point{X: -90, Y: -90}, ptOut: f32.Point{X: +14.142137, Y: +14.142137}},
// l<0 && X=-Y
{l: -20, ptIn: f32.Point{X: +90, Y: -90}, ptOut: f32.Point{X: -14.142137, Y: +14.142137}},
{l: -20, ptIn: f32.Point{X: +30, Y: -30}, ptOut: f32.Point{X: -14.142136, Y: +14.142136}},
{l: -20, ptIn: f32.Point{X: +20, Y: -20}, ptOut: f32.Point{X: -14.142136, Y: +14.142136}},
{l: -20, ptIn: f32.Point{X: +10, Y: -10}, ptOut: f32.Point{X: -14.142136, Y: +14.142136}},
{l: -20, ptIn: f32.Point{X: -10, Y: +10}, ptOut: f32.Point{X: +14.142136, Y: -14.142136}},
{l: -20, ptIn: f32.Point{X: -20, Y: +20}, ptOut: f32.Point{X: +14.142136, Y: -14.142136}},
{l: -20, ptIn: f32.Point{X: -30, Y: +30}, ptOut: f32.Point{X: +14.142136, Y: -14.142136}},
{l: -20, ptIn: f32.Point{X: -90, Y: +90}, ptOut: f32.Point{X: +14.142137, Y: -14.142137}},
// l!=0 && Hypot=l
{l: 5, ptIn: f32.Point{X: 3, Y: 4}, ptOut: f32.Point{X: 3, Y: 4}},
{l: 5, ptIn: f32.Point{X: 3, Y: -4}, ptOut: f32.Point{X: 3, Y: -4}},
{l: 5, ptIn: f32.Point{X: -3, Y: -4}, ptOut: f32.Point{X: -3, Y: -4}},
{l: 5, ptIn: f32.Point{X: -3, Y: 4}, ptOut: f32.Point{X: -3, Y: 4}},
{l: -5, ptIn: f32.Point{X: 3, Y: 4}, ptOut: f32.Point{X: -3, Y: -4}},
{l: -5, ptIn: f32.Point{X: 3, Y: -4}, ptOut: f32.Point{X: -3, Y: 4}},
{l: -5, ptIn: f32.Point{X: -3, Y: -4}, ptOut: f32.Point{X: 3, Y: 4}},
{l: -5, ptIn: f32.Point{X: -3, Y: 4}, ptOut: f32.Point{X: 3, Y: -4}},
}
for i, s := range scenarios {
t.Run(strconv.Itoa(i), func(t *testing.T) {
actual := normPt(s.ptIn, s.l)
if actual != s.ptOut {
t.Errorf("in: %v*%v, expected: %v, actual: %v", s.l, s.ptIn, s.ptOut, actual)
}
})
}
}
func BenchmarkSplitCubic(b *testing.B) { func BenchmarkSplitCubic(b *testing.B) {
type scenario struct { type scenario struct {
segments int segments int
@@ -157,7 +52,7 @@ func BenchmarkSplitCubic(b *testing.B) {
from, ctrl0, ctrl1, to := s.from, s.ctrl0, s.ctrl1, s.to from, ctrl0, ctrl1, to := s.from, s.ctrl0, s.ctrl1, s.to
quads := make([]QuadSegment, s.segments) quads := make([]QuadSegment, s.segments)
b.ResetTimer() b.ResetTimer()
for b.Loop() { for i := 0; i < b.N; i++ {
quads = SplitCubic(from, ctrl0, ctrl1, to, quads[:0]) quads = SplitCubic(from, ctrl0, ctrl1, to, quads[:0])
} }
if len(quads) != s.segments { if len(quads) != s.segments {
+7 -7
View File
@@ -385,7 +385,6 @@ static VkResult vkQueuePresentKHR(PFN_vkQueuePresentKHR f, VkQueue queue, const
} }
*/ */
import "C" import "C"
import ( import (
"errors" "errors"
"fmt" "fmt"
@@ -1168,6 +1167,7 @@ func CreateFramebuffer(d Device, rp RenderPass, view ImageView, width, height in
return nilFramebuffer, fmt.Errorf("vulkan: vkCreateFramebuffer: %w", err) return nilFramebuffer, fmt.Errorf("vulkan: vkCreateFramebuffer: %w", err)
} }
return fbo, nil return fbo, nil
} }
func DestroyFramebuffer(d Device, f Framebuffer) { func DestroyFramebuffer(d Device, f Framebuffer) {
@@ -1893,27 +1893,27 @@ func BuildWriteDescriptorSetBuffer(set DescriptorSet, binding int, typ Descripto
} }
} }
func PushConstantRangeStageFlags(r PushConstantRange) ShaderStageFlags { func (r PushConstantRange) StageFlags() ShaderStageFlags {
return r.stageFlags return r.stageFlags
} }
func PushConstantRangeOffset(r PushConstantRange) int { func (r PushConstantRange) Offset() int {
return int(r.offset) return int(r.offset)
} }
func PushConstantRangeSize(r PushConstantRange) int { func (r PushConstantRange) Size() int {
return int(r.size) return int(r.size)
} }
func QueueFamilyPropertiesFlags(p QueueFamilyProperties) QueueFlags { func (p QueueFamilyProperties) Flags() QueueFlags {
return p.queueFlags return p.queueFlags
} }
func SurfaceCapabilitiesMinExtent(c SurfaceCapabilities) image.Point { func (c SurfaceCapabilities) MinExtent() image.Point {
return image.Pt(int(c.minImageExtent.width), int(c.minImageExtent.height)) return image.Pt(int(c.minImageExtent.width), int(c.minImageExtent.height))
} }
func SurfaceCapabilitiesMaxExtent(c SurfaceCapabilities) image.Point { func (c SurfaceCapabilities) MaxExtent() image.Point {
return image.Pt(int(c.maxImageExtent.width), int(c.maxImageExtent.height)) return image.Pt(int(c.maxImageExtent.width), int(c.maxImageExtent.height))
} }
-1
View File
@@ -17,7 +17,6 @@ static VkResult vkCreateAndroidSurfaceKHR(PFN_vkCreateAndroidSurfaceKHR f, VkIns
} }
*/ */
import "C" import "C"
import ( import (
"fmt" "fmt"
"unsafe" "unsafe"
-1
View File
@@ -19,7 +19,6 @@ static VkResult vkCreateWaylandSurfaceKHR(PFN_vkCreateWaylandSurfaceKHR f, VkIns
} }
*/ */
import "C" import "C"
import ( import (
"fmt" "fmt"
"unsafe" "unsafe"
-1
View File
@@ -17,7 +17,6 @@ static VkResult vkCreateXlibSurfaceKHR(PFN_vkCreateXlibSurfaceKHR f, VkInstance
} }
*/ */
import "C" import "C"
import ( import (
"fmt" "fmt"
"unsafe" "unsafe"
+1 -1
View File
@@ -10,7 +10,7 @@ import (
// Tag is the stable identifier for an event handler. // Tag is the stable identifier for an event handler.
// For a handler h, the tag is typically &h. // For a handler h, the tag is typically &h.
type Tag any type Tag interface{}
// Event is the marker interface for events. // Event is the marker interface for events.
type Event interface { type Event interface {
+4 -3
View File
@@ -4,7 +4,6 @@ package input
import ( import (
"io" "io"
"slices"
"gioui.org/io/clipboard" "gioui.org/io/clipboard"
"gioui.org/io/event" "gioui.org/io/event"
@@ -61,8 +60,10 @@ func (q *clipboardQueue) ProcessWriteClipboard(req clipboard.WriteCmd) {
} }
func (q *clipboardQueue) ProcessReadClipboard(state clipboardState, tag event.Tag) clipboardState { func (q *clipboardQueue) ProcessReadClipboard(state clipboardState, tag event.Tag) clipboardState {
if slices.Contains(state.receivers, tag) { for _, k := range state.receivers {
return state if k == tag {
return state
}
} }
n := len(state.receivers) n := len(state.receivers)
state.receivers = append(state.receivers[:n:n], tag) state.receivers = append(state.receivers[:n:n], tag)
+1 -1
View File
@@ -52,7 +52,7 @@ func TestQueueProcessReadClipboard(t *testing.T) {
assertClipboardReadCmd(t, r, 1) assertClipboardReadCmd(t, r, 1)
ops.Reset() ops.Reset()
for range 3 { for i := 0; i < 3; i++ {
// No ReadCmd // No ReadCmd
// One receiver must still wait for response // One receiver must still wait for response
+4 -4
View File
@@ -4,7 +4,6 @@ package input
import ( import (
"image" "image"
"slices"
"sort" "sort"
"gioui.org/f32" "gioui.org/f32"
@@ -281,7 +280,6 @@ func (q *keyQueue) Focus(handlers map[event.Tag]*handler, state keyState, focus
return state, nil return state, nil
} }
state.content = EditorState{} state.content = EditorState{}
state.content.Selection.Transform = f32.AffineId()
var evts []taggedEvent var evts []taggedEvent
if state.focus != nil { if state.focus != nil {
evts = append(evts, taggedEvent{tag: state.focus, event: key.FocusEvent{Focus: false}}) evts = append(evts, taggedEvent{tag: state.focus, event: key.FocusEvent{Focus: false}})
@@ -306,8 +304,10 @@ func (s keyState) softKeyboard(show bool) keyState {
} }
func (k *keyFilter) Add(f key.Filter) { func (k *keyFilter) Add(f key.Filter) {
if slices.Contains(*k, f) { for _, f2 := range *k {
return if f == f2 {
return
}
} }
*k = append(*k, f) *k = append(*k, f)
} }
+36 -26
View File
@@ -5,7 +5,6 @@ package input
import ( import (
"image" "image"
"io" "io"
"slices"
"gioui.org/f32" "gioui.org/f32"
f32internal "gioui.org/internal/f32" f32internal "gioui.org/internal/f32"
@@ -144,9 +143,7 @@ const (
) )
func (c *pointerCollector) resetState() { func (c *pointerCollector) resetState() {
c.state = collectState{ c.state = collectState{}
t: f32.AffineId(),
}
c.nodeStack = c.nodeStack[:0] c.nodeStack = c.nodeStack[:0]
// Pop every node except the root. // Pop every node except the root.
if len(c.q.hitTree) > 0 { if len(c.q.hitTree) > 0 {
@@ -260,11 +257,6 @@ func (q *pointerQueue) grab(state pointerState, req pointer.GrabCmd) (pointerSta
if !p.pressed || p.id != req.ID { if !p.pressed || p.id != req.ID {
continue continue
} }
// Verify that the grabber is among the handlers.
found := slices.Contains(p.handlers, req.Tag)
if !found {
continue
}
// Drop other handlers that lost their grab. // Drop other handlers that lost their grab.
for i := len(p.handlers) - 1; i >= 0; i-- { for i := len(p.handlers) - 1; i >= 0; i-- {
if tag := p.handlers[i]; tag != req.Tag { if tag := p.handlers[i]; tag != req.Tag {
@@ -290,13 +282,17 @@ func (c *pointerCollector) inputOp(tag event.Tag, state *pointerHandler) {
func (p *pointerFilter) Add(f event.Filter) { func (p *pointerFilter) Add(f event.Filter) {
switch f := f.(type) { switch f := f.(type) {
case transfer.SourceFilter: case transfer.SourceFilter:
if slices.Contains(p.sourceMimes, f.Type) { for _, m := range p.sourceMimes {
return if m == f.Type {
return
}
} }
p.sourceMimes = append(p.sourceMimes, f.Type) p.sourceMimes = append(p.sourceMimes, f.Type)
case transfer.TargetFilter: case transfer.TargetFilter:
if slices.Contains(p.targetMimes, f.Type) { for _, m := range p.targetMimes {
return if m == f.Type {
return
}
} }
p.targetMimes = append(p.targetMimes, f.Type) p.targetMimes = append(p.targetMimes, f.Type)
case pointer.Filter: case pointer.Filter:
@@ -313,12 +309,16 @@ func (p *pointerFilter) Matches(e event.Event) bool {
case transfer.CancelEvent, transfer.InitiateEvent: case transfer.CancelEvent, transfer.InitiateEvent:
return len(p.sourceMimes) > 0 || len(p.targetMimes) > 0 return len(p.sourceMimes) > 0 || len(p.targetMimes) > 0
case transfer.RequestEvent: case transfer.RequestEvent:
if slices.Contains(p.sourceMimes, e.Type) { for _, t := range p.sourceMimes {
return true if t == e.Type {
return true
}
} }
case transfer.DataEvent: case transfer.DataEvent:
if slices.Contains(p.targetMimes, e.Type) { for _, t := range p.targetMimes {
return true if t == e.Type {
return true
}
} }
} }
return false return false
@@ -413,7 +413,7 @@ func (q *pointerQueue) offerData(handlers map[event.Tag]*handler, state pointerS
}, },
}}) }})
} }
state.pointers = slices.Clone(state.pointers) state.pointers = append([]pointerInfo{}, state.pointers...)
state.pointers[i], evts = q.deliverTransferCancelEvent(handlers, p, evts) state.pointers[i], evts = q.deliverTransferCancelEvent(handlers, p, evts)
break break
} }
@@ -605,7 +605,7 @@ func (q *pointerQueue) reset() {
for k, ids := range q.semantic.contentIDs { for k, ids := range q.semantic.contentIDs {
for i := len(ids) - 1; i >= 0; i-- { for i := len(ids) - 1; i >= 0; i-- {
if !ids[i].used { if !ids[i].used {
ids = slices.Delete(ids, i, i+1) ids = append(ids[:i], ids[i+1:]...)
} else { } else {
ids[i].used = false ids[i].used = false
} }
@@ -636,7 +636,7 @@ func (q *pointerQueue) Frame(handlers map[event.Tag]*handler, state pointerState
changed := false changed := false
p, evts, state.cursor, changed = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, p.last) p, evts, state.cursor, changed = q.deliverEnterLeaveEvents(handlers, state.cursor, p, evts, p.last)
if changed { if changed {
state.pointers = slices.Clone(state.pointers) state.pointers = append([]pointerInfo{}, state.pointers...)
state.pointers[i] = p state.pointers[i] = p
} }
} }
@@ -772,17 +772,19 @@ func (q *pointerQueue) Push(handlers map[event.Tag]*handler, state pointerState,
if !p.pressed && len(p.entered) == 0 { if !p.pressed && len(p.entered) == 0 {
// No longer need to track pointer. // No longer need to track pointer.
state.pointers = slices.Concat(state.pointers[:pidx:pidx], state.pointers[pidx+1:]) state.pointers = append(state.pointers[:pidx:pidx], state.pointers[pidx+1:]...)
} else { } else {
state.pointers = slices.Clone(state.pointers) state.pointers = append([]pointerInfo{}, state.pointers...)
state.pointers[pidx] = p state.pointers[pidx] = p
} }
return state, evts return state, evts
} }
func (q *pointerQueue) deliverEvent(handlers map[event.Tag]*handler, p pointerInfo, evts []taggedEvent, e pointer.Event) []taggedEvent { func (q *pointerQueue) deliverEvent(handlers map[event.Tag]*handler, p pointerInfo, evts []taggedEvent, e pointer.Event) []taggedEvent {
foremost := true
if p.pressed && len(p.handlers) == 1 { if p.pressed && len(p.handlers) == 1 {
e.Priority = pointer.Grabbed e.Priority = pointer.Grabbed
foremost = false
} }
scroll := e.Scroll scroll := e.Scroll
for _, k := range p.handlers { for _, k := range p.handlers {
@@ -801,6 +803,10 @@ func (q *pointerQueue) deliverEvent(handlers map[event.Tag]*handler, p pointerIn
scroll, e.Scroll = f.clampScroll(scroll) scroll, e.Scroll = f.clampScroll(scroll)
} }
e := e e := e
if foremost {
foremost = false
e.Priority = pointer.Foremost
}
e.Position = q.invTransform(h.pointer.areaPlusOne-1, e.Position) e.Position = q.invTransform(h.pointer.areaPlusOne-1, e.Position)
evts = append(evts, taggedEvent{event: e, tag: k}) evts = append(evts, taggedEvent{event: e, tag: k})
} }
@@ -964,8 +970,10 @@ func searchTag(tags []event.Tag, tag event.Tag) (int, bool) {
// addHandler adds tag to the slice if not present. // addHandler adds tag to the slice if not present.
func addHandler(tags []event.Tag, tag event.Tag) []event.Tag { func addHandler(tags []event.Tag, tag event.Tag) []event.Tag {
if slices.Contains(tags, tag) { for _, t := range tags {
return tags if t == tag {
return tags
}
} }
return append(tags, tag) return append(tags, tag)
} }
@@ -973,8 +981,10 @@ func addHandler(tags []event.Tag, tag event.Tag) []event.Tag {
// firstMimeMatch returns the first type match between src and tgt. // firstMimeMatch returns the first type match between src and tgt.
func firstMimeMatch(src, tgt *pointerFilter) (first string, matched bool) { func firstMimeMatch(src, tgt *pointerFilter) (first string, matched bool) {
for _, m1 := range tgt.targetMimes { for _, m1 := range tgt.targetMimes {
if slices.Contains(src.sourceMimes, m1) { for _, m2 := range src.sourceMimes {
return m1, true if m1 == m2 {
return m1, true
}
} }
} }
return "", false return "", false
+14 -55
View File
@@ -97,39 +97,6 @@ func TestPointerDragNegative(t *testing.T) {
assertEventPointerTypeSequence(t, events(&r, -1, f), pointer.Enter, pointer.Press, pointer.Leave, pointer.Drag) assertEventPointerTypeSequence(t, events(&r, -1, f), pointer.Enter, pointer.Press, pointer.Leave, pointer.Drag)
} }
func TestIgnoredGrab(t *testing.T) {
handler1 := new(int)
handler2 := new(int)
var ops op.Ops
filter := func(t event.Tag) event.Filter {
return pointer.Filter{Target: t, Kinds: pointer.Press | pointer.Release | pointer.Cancel}
}
event.Op(&ops, handler1)
event.Op(&ops, handler2)
var r Router
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler1)), pointer.Cancel)
assertEventPointerTypeSequence(t, events(&r, -1, filter(handler2)), pointer.Cancel)
r.Frame(&ops)
r.Queue(
pointer.Event{
Kind: pointer.Press,
Position: f32.Pt(50, 50),
},
pointer.Event{
Kind: pointer.Release,
Position: f32.Pt(50, 50),
},
)
assertEventPointerTypeSequence(t, events(&r, 1, filter(handler1)), pointer.Press)
assertEventPointerTypeSequence(t, events(&r, 1, filter(handler2)), pointer.Press)
r.Source().Execute(pointer.GrabCmd{Tag: handler1})
r.Source().Execute(pointer.GrabCmd{Tag: handler2})
assertEventPointerTypeSequence(t, events(&r, 1, filter(handler1)), pointer.Release)
assertEventPointerTypeSequence(t, events(&r, 1, filter(handler2)), pointer.Cancel)
}
func TestPointerGrab(t *testing.T) { func TestPointerGrab(t *testing.T) {
handler1 := new(int) handler1 := new(int)
handler2 := new(int) handler2 := new(int)
@@ -400,9 +367,9 @@ func TestPointerPriority(t *testing.T) {
assertEventPointerTypeSequence(t, hev1, pointer.Scroll, pointer.Scroll) assertEventPointerTypeSequence(t, hev1, pointer.Scroll, pointer.Scroll)
assertEventPointerTypeSequence(t, hev2, pointer.Scroll) assertEventPointerTypeSequence(t, hev2, pointer.Scroll)
assertEventPointerTypeSequence(t, hev3, pointer.Scroll) assertEventPointerTypeSequence(t, hev3, pointer.Scroll)
assertEventPriorities(t, hev1, pointer.Shared, pointer.Shared) assertEventPriorities(t, hev1, pointer.Shared, pointer.Foremost)
assertEventPriorities(t, hev2, pointer.Shared) assertEventPriorities(t, hev2, pointer.Foremost)
assertEventPriorities(t, hev3, pointer.Shared) assertEventPriorities(t, hev3, pointer.Foremost)
assertScrollEvent(t, hev1[0], f32.Pt(30, 0)) assertScrollEvent(t, hev1[0], f32.Pt(30, 0))
assertScrollEvent(t, hev2[0], f32.Pt(20, 0)) assertScrollEvent(t, hev2[0], f32.Pt(20, 0))
assertScrollEvent(t, hev1[1], f32.Pt(50, 0)) assertScrollEvent(t, hev1[1], f32.Pt(50, 0))
@@ -711,31 +678,26 @@ func TestCursor(t *testing.T) {
cursors []pointer.Cursor cursors []pointer.Cursor
want pointer.Cursor want pointer.Cursor
}{ }{
{ {label: "no movement",
label: "no movement",
cursors: []pointer.Cursor{pointer.CursorPointer}, cursors: []pointer.Cursor{pointer.CursorPointer},
want: pointer.CursorDefault, want: pointer.CursorDefault,
}, },
{ {label: "move inside",
label: "move inside",
cursors: []pointer.Cursor{pointer.CursorPointer}, cursors: []pointer.Cursor{pointer.CursorPointer},
events: _at(50, 50), events: _at(50, 50),
want: pointer.CursorPointer, want: pointer.CursorPointer,
}, },
{ {label: "move outside",
label: "move outside",
cursors: []pointer.Cursor{pointer.CursorPointer}, cursors: []pointer.Cursor{pointer.CursorPointer},
events: _at(200, 200), events: _at(200, 200),
want: pointer.CursorDefault, want: pointer.CursorDefault,
}, },
{ {label: "move back inside",
label: "move back inside",
cursors: []pointer.Cursor{pointer.CursorPointer}, cursors: []pointer.Cursor{pointer.CursorPointer},
events: _at(50, 50), events: _at(50, 50),
want: pointer.CursorPointer, want: pointer.CursorPointer,
}, },
{ {label: "send key events while inside",
label: "send key events while inside",
cursors: []pointer.Cursor{pointer.CursorPointer}, cursors: []pointer.Cursor{pointer.CursorPointer},
events: []event.Event{ events: []event.Event{
key.Event{Name: "A", State: key.Press}, key.Event{Name: "A", State: key.Press},
@@ -743,8 +705,7 @@ func TestCursor(t *testing.T) {
}, },
want: pointer.CursorPointer, want: pointer.CursorPointer,
}, },
{ {label: "send key events while outside",
label: "send key events while outside",
cursors: []pointer.Cursor{pointer.CursorPointer}, cursors: []pointer.Cursor{pointer.CursorPointer},
events: append( events: append(
_at(200, 200), _at(200, 200),
@@ -753,8 +714,7 @@ func TestCursor(t *testing.T) {
), ),
want: pointer.CursorDefault, want: pointer.CursorDefault,
}, },
{ {label: "add new input on top while inside",
label: "add new input on top while inside",
cursors: []pointer.Cursor{pointer.CursorPointer, pointer.CursorCrosshair}, cursors: []pointer.Cursor{pointer.CursorPointer, pointer.CursorCrosshair},
events: append( events: append(
_at(50, 50), _at(50, 50),
@@ -765,8 +725,7 @@ func TestCursor(t *testing.T) {
), ),
want: pointer.CursorCrosshair, want: pointer.CursorCrosshair,
}, },
{ {label: "remove input on top while inside",
label: "remove input on top while inside",
cursors: []pointer.Cursor{pointer.CursorPointer}, cursors: []pointer.Cursor{pointer.CursorPointer},
events: append( events: append(
_at(50, 50), _at(50, 50),
@@ -1151,7 +1110,7 @@ func TestPartialEvent(t *testing.T) {
key.FocusEvent{}, pointer.Event{Kind: pointer.Press, Source: pointer.Mouse, Priority: pointer.Shared}) key.FocusEvent{}, pointer.Event{Kind: pointer.Press, Source: pointer.Mouse, Priority: pointer.Shared})
r.Source().Execute(key.FocusCmd{Tag: 1}) r.Source().Execute(key.FocusCmd{Tag: 1})
assertEventSequence(t, events(&r, -1, pointer.Filter{Target: 2, Kinds: pointer.Press}), assertEventSequence(t, events(&r, -1, pointer.Filter{Target: 2, Kinds: pointer.Press}),
pointer.Event{Kind: pointer.Press, Source: pointer.Mouse, Priority: pointer.Shared}) 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.
@@ -1297,7 +1256,7 @@ func BenchmarkRouterAdd(b *testing.B) {
handlerCount := i handlerCount := i
b.Run(fmt.Sprintf("%d-handlers", i), func(b *testing.B) { b.Run(fmt.Sprintf("%d-handlers", i), func(b *testing.B) {
handlers := make([]event.Tag, handlerCount) handlers := make([]event.Tag, handlerCount)
for i := range handlerCount { for i := 0; i < handlerCount; i++ {
h := new(int) h := new(int)
*h = i *h = i
handlers[i] = h handlers[i] = h
@@ -1319,7 +1278,7 @@ func BenchmarkRouterAdd(b *testing.B) {
r.Frame(&ops) r.Frame(&ops)
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
for b.Loop() { for i := 0; i < b.N; i++ {
r.Queue( r.Queue(
pointer.Event{ pointer.Event{
Kind: pointer.Move, Kind: pointer.Move,
+13 -25
View File
@@ -5,7 +5,6 @@ package input
import ( import (
"image" "image"
"io" "io"
"slices"
"strings" "strings"
"time" "time"
@@ -61,10 +60,9 @@ type Router struct {
} }
// Source implements the interface between a Router and user interface widgets. // Source implements the interface between a Router and user interface widgets.
// The zero-value Source is disabled. // The value Source is disabled.
type Source struct { type Source struct {
r *Router r *Router
disabled bool
} }
// Command represents a request such as moving the focus, or initiating a clipboard read. // Command represents a request such as moving the focus, or initiating a clipboard read.
@@ -179,17 +177,10 @@ func (s Source) Execute(c Command) {
s.r.execute(c) s.r.execute(c)
} }
// Disabled returns a copy of this source that don't deliver any events.
func (s Source) Disabled() Source {
s2 := s
s2.disabled = true
return s2
}
// Enabled reports whether the source is enabled. Only enabled // Enabled reports whether the source is enabled. Only enabled
// Sources deliver events. // Sources deliver events and respond to commands.
func (s Source) Enabled() bool { func (s Source) Enabled() bool {
return s.r != nil && !s.disabled return s.r != nil
} }
// Focused reports whether tag is focused, according to the most recent // Focused reports whether tag is focused, according to the most recent
@@ -202,7 +193,6 @@ func (s Source) Focused(tag event.Tag) bool {
} }
// Event returns the next event that matches at least one of filters. // Event returns the next event that matches at least one of filters.
// If the source is disabled, no events will be reported.
func (s Source) Event(filters ...event.Filter) (event.Event, bool) { func (s Source) Event(filters ...event.Filter) (event.Event, bool) {
if !s.Enabled() { if !s.Enabled() {
return nil, false return nil, false
@@ -303,7 +293,7 @@ func (q *Router) Event(filters ...event.Filter) (event.Event, bool) {
} }
} }
if match { if match {
change.events = slices.Delete(change.events, j, j+1) change.events = append(change.events[:j], change.events[j+1:]...)
// Fast forward state to last matched. // Fast forward state to last matched.
q.collapseState(i) q.collapseState(i)
return evt.event, true return evt.event, true
@@ -628,11 +618,11 @@ func (q *Router) RevealFocus(viewport image.Rectangle) {
viewport = q.pointer.queue.ClipFor(area, viewport) viewport = q.pointer.queue.ClipFor(area, viewport)
topleft := bounds.Min.Sub(viewport.Min) topleft := bounds.Min.Sub(viewport.Min)
topleft = maxPoint(topleft, bounds.Max.Sub(viewport.Max)) topleft = max(topleft, bounds.Max.Sub(viewport.Max))
topleft = minPoint(image.Pt(0, 0), topleft) topleft = min(image.Pt(0, 0), topleft)
bottomright := bounds.Max.Sub(viewport.Max) bottomright := bounds.Max.Sub(viewport.Max)
bottomright = minPoint(bottomright, bounds.Min.Sub(viewport.Min)) bottomright = min(bottomright, bounds.Min.Sub(viewport.Min))
bottomright = maxPoint(image.Pt(0, 0), bottomright) bottomright = max(image.Pt(0, 0), bottomright)
s := topleft s := topleft
if s.X == 0 { if s.X == 0 {
s.X = bottomright.X s.X = bottomright.X
@@ -659,7 +649,7 @@ func (q *Router) ScrollFocus(dist image.Point) {
})) }))
} }
func maxPoint(p1, p2 image.Point) image.Point { func max(p1, p2 image.Point) image.Point {
m := p1 m := p1
if p2.X > m.X { if p2.X > m.X {
m.X = p2.X m.X = p2.X
@@ -670,7 +660,7 @@ func maxPoint(p1, p2 image.Point) image.Point {
return m return m
} }
func minPoint(p1, p2 image.Point) image.Point { func min(p1, p2 image.Point) image.Point {
m := p1 m := p1
if p2.X < m.X { if p2.X < m.X {
m.X = p2.X m.X = p2.X
@@ -779,15 +769,13 @@ func (q *Router) collect() {
pc.Reset() pc.Reset()
kq := &q.key.queue kq := &q.key.queue
q.key.queue.Reset() q.key.queue.Reset()
t := f32.AffineId() var t f32.Affine2D
for encOp, ok := q.reader.Decode(); ok; encOp, ok = q.reader.Decode() { for encOp, ok := q.reader.Decode(); ok; encOp, ok = q.reader.Decode() {
switch ops.OpType(encOp.Data[0]) { switch ops.OpType(encOp.Data[0]) {
case ops.TypeSave: case ops.TypeSave:
id := ops.DecodeSave(encOp.Data) id := ops.DecodeSave(encOp.Data)
if extra := id - len(q.savedTrans) + 1; extra > 0 { if extra := id - len(q.savedTrans) + 1; extra > 0 {
for range extra { q.savedTrans = append(q.savedTrans, make([]f32.Affine2D, extra)...)
q.savedTrans = append(q.savedTrans, f32.AffineId())
}
} }
q.savedTrans[id] = t q.savedTrans[id] = t
case ops.TypeLoad: case ops.TypeLoad:
+1 -1
View File
@@ -15,7 +15,7 @@ func TestNoFilterAllocs(t *testing.T) {
s := r.Source() s := r.Source()
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
for b.Loop() { for i := 0; i < b.N; i++ {
s.Event(pointer.Filter{}) s.Event(pointer.Filter{})
} }
}) })
+1 -1
View File
@@ -125,7 +125,7 @@ func verifyTree(t *testing.T, parent SemanticID, n SemanticNode) {
} }
func printTree(indent int, n SemanticNode) { func printTree(indent int, n SemanticNode) {
for range indent { for i := 0; i < indent; i++ {
fmt.Print("\t") fmt.Print("\t")
} }
fmt.Printf("%d: %+v\n", n.ID, n.Desc) fmt.Printf("%d: %+v\n", n.ID, n.Desc)
+8 -14
View File
@@ -19,7 +19,7 @@ type Event struct {
Source Source Source Source
// PointerID is the id for the pointer and can be used // PointerID is the id for the pointer and can be used
// to track a particular pointer from Press to // to track a particular pointer from Press to
// Release. // Release or Cancel.
PointerID ID PointerID ID
// Priority is the priority of the receiving handler // Priority is the priority of the receiving handler
// for this event. // for this event.
@@ -43,7 +43,8 @@ type Event struct {
// PassOp sets the pass-through mode. InputOps added while the pass-through // PassOp sets the pass-through mode. InputOps added while the pass-through
// mode is set don't block events to siblings. // mode is set don't block events to siblings.
type PassOp struct{} type PassOp struct {
}
// PassStack represents a PassOp on the pass stack. // PassStack represents a PassOp on the pass stack.
type PassStack struct { type PassStack struct {
@@ -206,6 +207,9 @@ const (
// Shared priority is for handlers that // Shared priority is for handlers that
// are part of a matching set larger than 1. // are part of a matching set larger than 1.
Shared Priority = iota Shared Priority = iota
// Foremost priority is like Shared, but the
// handler is the foremost of the matching set.
Foremost
// Grabbed is used for matching sets of size 1. // Grabbed is used for matching sets of size 1.
Grabbed Grabbed
) )
@@ -219,12 +223,6 @@ const (
ButtonSecondary ButtonSecondary
// ButtonTertiary is the tertiary button, usually the middle button. // ButtonTertiary is the tertiary button, usually the middle button.
ButtonTertiary ButtonTertiary
// ButtonQuaternary is the fourth button, usually used for browser
// navigation (backward)
ButtonQuaternary
// ButtonQuinary is the fifth button, usually used for browser
// navigation (forward)
ButtonQuinary
) )
func (s ScrollRange) Union(s2 ScrollRange) ScrollRange { func (s ScrollRange) Union(s2 ScrollRange) ScrollRange {
@@ -297,6 +295,8 @@ func (p Priority) String() string {
switch p { switch p {
case Shared: case Shared:
return "Shared" return "Shared"
case Foremost:
return "Foremost"
case Grabbed: case Grabbed:
return "Grabbed" return "Grabbed"
default: default:
@@ -332,12 +332,6 @@ func (b Buttons) String() string {
if b.Contain(ButtonTertiary) { if b.Contain(ButtonTertiary) {
strs = append(strs, "ButtonTertiary") strs = append(strs, "ButtonTertiary")
} }
if b.Contain(ButtonQuaternary) {
strs = append(strs, "ButtonQuaternary")
}
if b.Contain(ButtonQuinary) {
strs = append(strs, "ButtonQuinary")
}
return strings.Join(strs, "|") return strings.Join(strs, "|")
} }
+3 -6
View File
@@ -28,10 +28,6 @@ type Context struct {
// Interested users must look up and populate these values manually. // Interested users must look up and populate these values manually.
Locale system.Locale Locale system.Locale
// Values is a map of program global data associated with the context.
// It is not for use by widgets.
Values map[string]any
input.Source input.Source
*op.Ops *op.Ops
} }
@@ -46,8 +42,9 @@ func (c Context) Sp(v unit.Sp) int {
return c.Metric.Sp(v) return c.Metric.Sp(v)
} }
// Disabled returns a copy of this context that don't deliver any events. // Disabled returns a copy of this context with a disabled Source,
// blocking widgets from changing its state and receiving events.
func (c Context) Disabled() Context { func (c Context) Disabled() Context {
c.Source = c.Source.Disabled() c.Source = input.Source{}
return c return c
} }
+14 -24
View File
@@ -30,6 +30,10 @@ type FlexChild struct {
weight float32 weight float32
widget Widget widget Widget
// Scratch space.
call op.CallOp
dims Dimensions
} }
// Spacing determine the spacing mode for a Flex. // Spacing determine the spacing mode for a Flex.
@@ -84,20 +88,6 @@ func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions {
remaining := mainMax remaining := mainMax
var totalWeight float32 var totalWeight float32
cgtx := gtx cgtx := gtx
// Note: previously the scratch space was inside FlexChild.
// child.call.Add(gtx.Ops) confused the go escape analysis and caused the
// entired children slice to be allocated on the heap, including all widgets
// in it. This produced a lot of object allocations. Now the scratch space
// is separate from children, and for cases len(children) <= 32, we will
// allocate the scratch space on the stack. For cases len(children) > 32,
// only the scratch space gets allocated from the heap, during append.
type scratchSpace struct {
call op.CallOp
dims Dimensions
}
var scratchArray [32]scratchSpace
scratch := scratchArray[:0]
scratch = append(scratch, make([]scratchSpace, len(children))...)
// Lay out Rigid children. // Lay out Rigid children.
for i, child := range children { for i, child := range children {
if child.flex { if child.flex {
@@ -114,8 +104,8 @@ func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions {
if remaining < 0 { if remaining < 0 {
remaining = 0 remaining = 0
} }
scratch[i].call = c children[i].call = c
scratch[i].dims = dims children[i].dims = dims
} }
if w := f.WeightSum; w != 0 { if w := f.WeightSum; w != 0 {
totalWeight = w totalWeight = w
@@ -149,16 +139,16 @@ func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions {
if remaining < 0 { if remaining < 0 {
remaining = 0 remaining = 0
} }
scratch[i].call = c children[i].call = c
scratch[i].dims = dims children[i].dims = dims
} }
maxCross := crossMin maxCross := crossMin
var maxBaseline int var maxBaseline int
for _, scratchChild := range scratch { for _, child := range children {
if c := f.Axis.Convert(scratchChild.dims.Size).Y; c > maxCross { if c := f.Axis.Convert(child.dims.Size).Y; c > maxCross {
maxCross = c maxCross = c
} }
if b := scratchChild.dims.Size.Y - scratchChild.dims.Baseline; b > maxBaseline { if b := child.dims.Size.Y - child.dims.Baseline; b > maxBaseline {
maxBaseline = b maxBaseline = b
} }
} }
@@ -179,8 +169,8 @@ func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions {
mainSize += space / (len(children) * 2) mainSize += space / (len(children) * 2)
} }
} }
for i, scratchChild := range scratch { for i, child := range children {
dims := scratchChild.dims dims := child.dims
b := dims.Size.Y - dims.Baseline b := dims.Size.Y - dims.Baseline
var cross int var cross int
switch f.Alignment { switch f.Alignment {
@@ -195,7 +185,7 @@ func (f Flex) Layout(gtx Context, children ...FlexChild) Dimensions {
} }
pt := f.Axis.Convert(image.Pt(mainSize, cross)) pt := f.Axis.Convert(image.Pt(mainSize, cross))
trans := op.Offset(pt).Push(gtx.Ops) trans := op.Offset(pt).Push(gtx.Ops)
scratchChild.call.Add(gtx.Ops) child.call.Add(gtx.Ops)
trans.Pop() trans.Pop()
mainSize += f.Axis.Convert(dims.Size).X mainSize += f.Axis.Convert(dims.Size).X
if i < len(children)-1 { if i < len(children)-1 {
+6 -11
View File
@@ -29,8 +29,6 @@ type List struct {
ScrollToEnd bool ScrollToEnd bool
// Alignment is the cross axis alignment of list elements. // Alignment is the cross axis alignment of list elements.
Alignment Alignment Alignment Alignment
// ScrollAnyAxis allows any scroll axis to scroll the list, not just the main axis.
ScrollAnyAxis bool
cs Constraints cs Constraints
scroll gesture.Scroll scroll gesture.Scroll
@@ -161,19 +159,12 @@ func (l *List) update(gtx Context) {
max = 0 max = 0
} }
} }
xrange := pointer.ScrollRange{Min: min, Max: max} xrange := pointer.ScrollRange{Min: min, Max: max}
yrange := pointer.ScrollRange{} yrange := pointer.ScrollRange{}
if l.Axis == Vertical {
axis := gesture.Axis(l.Axis)
if l.ScrollAnyAxis {
axis = gesture.Both
yrange = xrange
} else if l.Axis == Vertical {
xrange, yrange = yrange, xrange xrange, yrange = yrange, xrange
} }
d := l.scroll.Update(gtx.Metric, gtx.Source, gtx.Now, axis, xrange, yrange) d := l.scroll.Update(gtx.Metric, gtx.Source, gtx.Now, gesture.Axis(l.Axis), xrange, yrange)
l.scrollDelta = d l.scrollDelta = d
l.Position.Offset += d l.Position.Offset += d
} }
@@ -317,6 +308,10 @@ func (l *List) layout(ops *op.Ops, macro op.MacroOp) Dimensions {
cross = (maxCross - sz.Y) / 2 cross = (maxCross - sz.Y) / 2
} }
childSize := sz.X childSize := sz.X
min := pos
if min < 0 {
min = 0
}
pt := l.Axis.Convert(image.Pt(pos, cross)) pt := l.Axis.Convert(image.Pt(pos, cross))
trans := op.Offset(pt).Push(ops) trans := op.Offset(pt).Push(ops)
child.call.Add(ops) child.call.Add(ops)
+6 -12
View File
@@ -88,8 +88,7 @@ func TestListPosition(t *testing.T) {
{label: "1 visible 0 hidden", num: 1, count: 1, last: 10}, {label: "1 visible 0 hidden", num: 1, count: 1, last: 10},
{label: "2 visible 0 hidden", num: 2, count: 2}, {label: "2 visible 0 hidden", num: 2, count: 2},
{label: "2 visible 1 hidden", num: 3, count: 2}, {label: "2 visible 1 hidden", num: 3, count: 2},
{ {label: "3 visible 0 hidden small scroll", num: 3, count: 3, offset: 5, last: -5,
label: "3 visible 0 hidden small scroll", num: 3, count: 3, offset: 5, last: -5,
scroll: _s( scroll: _s(
pointer.Event{ pointer.Event{
Source: pointer.Mouse, Source: pointer.Mouse,
@@ -108,10 +107,8 @@ func TestListPosition(t *testing.T) {
Kind: pointer.Release, Kind: pointer.Release,
Position: f32.Pt(5, 0), Position: f32.Pt(5, 0),
}, },
), )},
}, {label: "3 visible 0 hidden small scroll 2", num: 3, count: 3, offset: 3, last: -7,
{
label: "3 visible 0 hidden small scroll 2", num: 3, count: 3, offset: 3, last: -7,
scroll: _s( scroll: _s(
pointer.Event{ pointer.Event{
Source: pointer.Mouse, Source: pointer.Mouse,
@@ -130,10 +127,8 @@ func TestListPosition(t *testing.T) {
Kind: pointer.Release, Kind: pointer.Release,
Position: f32.Pt(5, 0), Position: f32.Pt(5, 0),
}, },
), )},
}, {label: "2 visible 1 hidden large scroll", num: 3, count: 2, first: 1,
{
label: "2 visible 1 hidden large scroll", num: 3, count: 2, first: 1,
scroll: _s( scroll: _s(
pointer.Event{ pointer.Event{
Source: pointer.Mouse, Source: pointer.Mouse,
@@ -152,8 +147,7 @@ func TestListPosition(t *testing.T) {
Kind: pointer.Release, Kind: pointer.Release,
Position: f32.Pt(15, 0), Position: f32.Pt(15, 0),
}, },
), )},
},
} { } {
t.Run(tc.label, func(t *testing.T) { t.Run(tc.label, func(t *testing.T) {
gtx.Ops.Reset() gtx.Ops.Reset()
+12 -22
View File
@@ -20,6 +20,10 @@ type Stack struct {
type StackChild struct { type StackChild struct {
expanded bool expanded bool
widget Widget widget Widget
// Scratch space.
call op.CallOp
dims Dimensions
} }
// Stacked returns a Stack child that is laid out with no minimum // Stacked returns a Stack child that is laid out with no minimum
@@ -48,20 +52,6 @@ func (s Stack) Layout(gtx Context, children ...StackChild) Dimensions {
// First lay out Stacked children. // First lay out Stacked children.
cgtx := gtx cgtx := gtx
cgtx.Constraints.Min = image.Point{} cgtx.Constraints.Min = image.Point{}
// Note: previously the scratch space was inside StackChild.
// child.call.Add(gtx.Ops) confused the go escape analysis and caused the
// entired children slice to be allocated on the heap, including all widgets
// in it. This produced a lot of object allocations. Now the scratch space
// is separate from children, and for cases len(children) <= 32, we will
// allocate the scratch space on the stack. For cases len(children) > 32,
// only the scratch space gets allocated from the heap, during append.
type scratchSpace struct {
call op.CallOp
dims Dimensions
}
var scratchArray [32]scratchSpace
scratch := scratchArray[:0]
scratch = append(scratch, make([]scratchSpace, len(children))...)
for i, w := range children { for i, w := range children {
if w.expanded { if w.expanded {
continue continue
@@ -75,8 +65,8 @@ func (s Stack) Layout(gtx Context, children ...StackChild) Dimensions {
if h := dims.Size.Y; h > maxSZ.Y { if h := dims.Size.Y; h > maxSZ.Y {
maxSZ.Y = h maxSZ.Y = h
} }
scratch[i].call = call children[i].call = call
scratch[i].dims = dims children[i].dims = dims
} }
// Then lay out Expanded children. // Then lay out Expanded children.
for i, w := range children { for i, w := range children {
@@ -93,14 +83,14 @@ func (s Stack) Layout(gtx Context, children ...StackChild) Dimensions {
if h := dims.Size.Y; h > maxSZ.Y { if h := dims.Size.Y; h > maxSZ.Y {
maxSZ.Y = h maxSZ.Y = h
} }
scratch[i].call = call children[i].call = call
scratch[i].dims = dims children[i].dims = dims
} }
maxSZ = gtx.Constraints.Constrain(maxSZ) maxSZ = gtx.Constraints.Constrain(maxSZ)
var baseline int var baseline int
for _, scratchChild := range scratch { for _, ch := range children {
sz := scratchChild.dims.Size sz := ch.dims.Size
var p image.Point var p image.Point
switch s.Alignment { switch s.Alignment {
case N, S, Center: case N, S, Center:
@@ -115,10 +105,10 @@ func (s Stack) Layout(gtx Context, children ...StackChild) Dimensions {
p.Y = maxSZ.Y - sz.Y p.Y = maxSZ.Y - sz.Y
} }
trans := op.Offset(p).Push(gtx.Ops) trans := op.Offset(p).Push(gtx.Ops)
scratchChild.call.Add(gtx.Ops) ch.call.Add(gtx.Ops)
trans.Pop() trans.Pop()
if baseline == 0 { if baseline == 0 {
if b := scratchChild.dims.Baseline; b != 0 { if b := ch.dims.Baseline; b != 0 {
baseline = b + maxSZ.Y - sz.Y - p.Y baseline = b + maxSZ.Y - sz.Y - p.Y
} }
} }
+4 -2
View File
@@ -17,8 +17,9 @@ func BenchmarkStack(b *testing.B) {
}, },
} }
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer()
for b.Loop() { for i := 0; i < b.N; i++ {
gtx.Ops.Reset() gtx.Ops.Reset()
Stack{}.Layout(gtx, Stack{}.Layout(gtx,
@@ -40,8 +41,9 @@ func BenchmarkBackground(b *testing.B) {
}, },
} }
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer()
for b.Loop() { for i := 0; i < b.N; i++ {
gtx.Ops.Reset() gtx.Ops.Reset()
Background{}.Layout(gtx, Background{}.Layout(gtx,
+4 -7
View File
@@ -214,9 +214,8 @@ func (p *Path) LineTo(to f32.Point) {
bo := binary.LittleEndian bo := binary.LittleEndian
bo.PutUint32(data[0:], uint32(p.contour)) bo.PutUint32(data[0:], uint32(p.contour))
p.cmd(data[4:], scene.Line(p.pen, to)) p.cmd(data[4:], scene.Line(p.pen, to))
p.expand(p.pen)
p.expand(to)
p.pen = to p.pen = to
p.expand(to)
} }
func (p *Path) cmd(data []byte, c scene.Command) { func (p *Path) cmd(data []byte, c scene.Command) {
@@ -264,10 +263,9 @@ func (p *Path) QuadTo(ctrl, to f32.Point) {
bo := binary.LittleEndian bo := binary.LittleEndian
bo.PutUint32(data[0:], uint32(p.contour)) bo.PutUint32(data[0:], uint32(p.contour))
p.cmd(data[4:], scene.Quad(p.pen, ctrl, to)) p.cmd(data[4:], scene.Quad(p.pen, ctrl, to))
p.expand(p.pen) p.pen = to
p.expand(ctrl) p.expand(ctrl)
p.expand(to) p.expand(to)
p.pen = to
} }
// ArcTo adds an elliptical arc to the path. The implied ellipse is defined // ArcTo adds an elliptical arc to the path. The implied ellipse is defined
@@ -277,7 +275,7 @@ func (p *Path) QuadTo(ctrl, to f32.Point) {
// negative clockwise. // negative clockwise.
func (p *Path) ArcTo(f1, f2 f32.Point, angle float32) { func (p *Path) ArcTo(f1, f2 f32.Point, angle float32) {
m, segments := stroke.ArcTransform(p.pen, f1, f2, angle) m, segments := stroke.ArcTransform(p.pen, f1, f2, angle)
for range segments { for i := 0; i < segments; i++ {
p0 := p.pen p0 := p.pen
p1 := m.Transform(p0) p1 := m.Transform(p0)
p2 := m.Transform(p1) p2 := m.Transform(p1)
@@ -309,11 +307,10 @@ func (p *Path) CubeTo(ctrl0, ctrl1, to f32.Point) {
bo := binary.LittleEndian bo := binary.LittleEndian
bo.PutUint32(data[0:], uint32(p.contour)) bo.PutUint32(data[0:], uint32(p.contour))
p.cmd(data[4:], scene.Cubic(p.pen, ctrl0, ctrl1, to)) p.cmd(data[4:], scene.Cubic(p.pen, ctrl0, ctrl1, to))
p.expand(p.pen) p.pen = to
p.expand(ctrl0) p.expand(ctrl0)
p.expand(ctrl1) p.expand(ctrl1)
p.expand(to) p.expand(to)
p.pen = to
} }
// Close closes the path contour. // Close closes the path contour.
-106
View File
@@ -1,106 +0,0 @@
// SPDX-License-Identifier: Unlicense OR MIT
package clip
import (
"gioui.org/f32"
"gioui.org/op"
"math"
"testing"
)
func TestPath_MoveTo_LineTo(t *testing.T) {
var ops op.Ops
p := Path{}
p.Begin(&ops)
startPoint := f32.Pt(32, 32)
endPoint := f32.Pt(64, 64)
p.MoveTo(startPoint)
p.LineTo(endPoint)
pathSpec := p.End()
minPoint := f32.Pt(32, 32)
maxPoint := f32.Pt(64, 64)
if pathSpec.bounds.Min == pathSpec.bounds.Max {
t.Errorf("zero path")
}
if pathSpec.bounds.Min != minPoint.Round() {
t.Errorf("pathSpec.bounds.Min = %v, want %v", pathSpec.bounds.Min, minPoint)
}
if pathSpec.bounds.Max != maxPoint.Round() {
t.Errorf("pathSpec.bounds.Max = %v, want %v", pathSpec.bounds.Max, maxPoint)
}
}
func TestPath_MoveTo_QuadTo(t *testing.T) {
var ops op.Ops
p := Path{}
p.Begin(&ops)
startPoint := f32.Pt(32, 32)
midPoint := f32.Pt(60, 60)
p.MoveTo(startPoint)
p.QuadTo(midPoint.Sub(f32.Pt(-4, 0)), midPoint.Sub(f32.Pt(0, -4)))
pathSpec := p.End()
minPoint := f32.Pt(32, 32)
maxPoint := f32.Pt(64, 64)
if pathSpec.bounds.Min == pathSpec.bounds.Max {
t.Errorf("zero path")
}
if pathSpec.bounds.Min != minPoint.Round() {
t.Errorf("pathSpec.bounds.Min = %v, want %v", pathSpec.bounds.Min, minPoint)
}
if pathSpec.bounds.Max != maxPoint.Round() {
t.Errorf("pathSpec.bounds.Max = %v, want %v", pathSpec.bounds.Max, maxPoint)
}
}
func TestPath_MoveTo_ArcTo(t *testing.T) {
// We need a tolerance here because of rounding errors.
tolerance := f32.Pt(1, 1)
var ops op.Ops
p := Path{}
p.Begin(&ops)
arcStartPoint := f32.Pt(48, 32)
arcCenterPoint := f32.Pt(48, 48)
p.MoveTo(arcStartPoint)
p.ArcTo(arcCenterPoint, arcCenterPoint, math.Pi*2)
pathSpec := p.End()
minPoint := f32.Pt(32, 32).Sub(tolerance).Round()
maxPoint := f32.Pt(64, 64).Add(tolerance).Round()
if pathSpec.bounds.Min == pathSpec.bounds.Max {
t.Errorf("zero path")
}
if pathSpec.bounds.Min.X < minPoint.X || pathSpec.bounds.Min.Y < minPoint.Y {
t.Errorf("pathSpec.bounds.Min = %v, want %v", pathSpec.bounds.Min, minPoint)
}
if pathSpec.bounds.Max.X > maxPoint.X || pathSpec.bounds.Max.Y > maxPoint.Y {
t.Errorf("pathSpec.bounds.Max = %v, want %v", pathSpec.bounds.Max, maxPoint)
}
}
func TestPath_MoveTo_CubeTo(t *testing.T) {
var ops op.Ops
p := Path{}
p.Begin(&ops)
startPoint := f32.Pt(32, 32)
midPoint := f32.Pt(48, 48)
endPoint := f32.Pt(64, 64)
p.MoveTo(startPoint)
p.CubeTo(midPoint.Sub(f32.Pt(-4, 0)), midPoint.Sub(f32.Pt(0, -4)), endPoint)
pathSpec := p.End()
minPoint := f32.Pt(32, 32)
maxPoint := f32.Pt(64, 64)
if pathSpec.bounds.Min == pathSpec.bounds.Max {
t.Errorf("zero path")
}
if pathSpec.bounds.Min != minPoint.Round() {
t.Errorf("pathSpec.bounds.Min = %v, want %v", pathSpec.bounds.Min, minPoint)
}
if pathSpec.bounds.Max != maxPoint.Round() {
t.Errorf("pathSpec.bounds.Max = %v, want %v", pathSpec.bounds.Max, maxPoint)
}
}
+1 -1
View File
@@ -183,7 +183,7 @@ func (c CallOp) Add(o *Ops) {
// Offset converts an offset to a TransformOp. // Offset converts an offset to a TransformOp.
func Offset(off image.Point) TransformOp { func Offset(off image.Point) TransformOp {
offf := f32.Pt(float32(off.X), float32(off.Y)) offf := f32.Pt(float32(off.X), float32(off.Y))
return Affine(f32.AffineId().Offset(offf)) return Affine(f32.Affine2D{}.Offset(offf))
} }
// Affine creates a TransformOp representing the transformation a. // Affine creates a TransformOp representing the transformation a.
+3 -2
View File
@@ -35,7 +35,7 @@ type ImageOp struct {
// handle is a key to uniquely identify this ImageOp // handle is a key to uniquely identify this ImageOp
// in a map of cached textures. // in a map of cached textures.
handle any handle interface{}
} }
// ColorOp sets the brush to a constant color. // ColorOp sets the brush to a constant color.
@@ -53,7 +53,8 @@ type LinearGradientOp struct {
} }
// PaintOp fills the current clip area with the current brush. // PaintOp fills the current clip area with the current brush.
type PaintOp struct{} type PaintOp struct {
}
// OpacityStack represents an opacity applied to all painting operations // OpacityStack represents an opacity applied to all painting operations
// until Pop is called. // until Pop is called.
+2 -1
View File
@@ -1,8 +1,9 @@
package text package text
import ( import (
"slices"
"testing" "testing"
"golang.org/x/exp/slices"
) )
func TestParser(t *testing.T) { func TestParser(t *testing.T) {
+102 -46
View File
@@ -9,14 +9,15 @@ import (
"io" "io"
"log" "log"
"os" "os"
"slices"
"github.com/go-text/typesetting/di" "github.com/go-text/typesetting/di"
"github.com/go-text/typesetting/font" "github.com/go-text/typesetting/font"
gotextot "github.com/go-text/typesetting/font/opentype"
"github.com/go-text/typesetting/fontscan" "github.com/go-text/typesetting/fontscan"
"github.com/go-text/typesetting/language" "github.com/go-text/typesetting/language"
"github.com/go-text/typesetting/opentype/api"
"github.com/go-text/typesetting/opentype/api/metadata"
"github.com/go-text/typesetting/shaping" "github.com/go-text/typesetting/shaping"
"golang.org/x/exp/slices"
"golang.org/x/image/math/fixed" "golang.org/x/image/math/fixed"
"golang.org/x/text/unicode/bidi" "golang.org/x/text/unicode/bidi"
@@ -56,6 +57,13 @@ func (l *document) reset() {
l.unreadRuneCount = 0 l.unreadRuneCount = 0
} }
func max(a, b int) int {
if a > b {
return a
}
return b
}
// A line contains the measurements of a line of text. // A line contains the measurements of a line of text.
type line struct { type line struct {
// runs contains sequences of shaped glyphs with common attributes. The order // runs contains sequences of shaped glyphs with common attributes. The order
@@ -193,7 +201,7 @@ type runLayout struct {
// Direction is the layout direction of the glyphs. // Direction is the layout direction of the glyphs.
Direction system.TextDirection Direction system.TextDirection
// face is the font face that the ID of each Glyph in the Layout refers to. // face is the font face that the ID of each Glyph in the Layout refers to.
face *font.Face face font.Face
// truncator indicates that this run is a text truncator standing in for remaining // truncator indicates that this run is a text truncator standing in for remaining
// text. // text.
truncator bool truncator bool
@@ -203,8 +211,8 @@ type runLayout struct {
type shaperImpl struct { type shaperImpl struct {
// Fields for tracking fonts/faces. // Fields for tracking fonts/faces.
fontMap *fontscan.FontMap fontMap *fontscan.FontMap
faces []*font.Face faces []font.Face
faceToIndex map[*font.Font]int faceToIndex map[font.Font]int
faceMeta []giofont.Font faceMeta []giofont.Font
defaultFaces []string defaultFaces []string
logger interface { logger interface {
@@ -246,7 +254,7 @@ func newShaperImpl(systemFonts bool, collection []FontFace) *shaperImpl {
var shaper shaperImpl var shaper shaperImpl
shaper.logger = newDebugLogger() shaper.logger = newDebugLogger()
shaper.fontMap = fontscan.NewFontMap(shaper.logger) shaper.fontMap = fontscan.NewFontMap(shaper.logger)
shaper.faceToIndex = make(map[*font.Font]int) shaper.faceToIndex = make(map[font.Font]int)
if systemFonts { if systemFonts {
str, err := os.UserCacheDir() str, err := os.UserCacheDir()
if err != nil { if err != nil {
@@ -274,7 +282,7 @@ func (s *shaperImpl) Load(f FontFace) {
s.addFace(f.Face.Face(), f.Font) s.addFace(f.Face.Face(), f.Font)
} }
func (s *shaperImpl) addFace(f *font.Face, md giofont.Font) { func (s *shaperImpl) addFace(f font.Face, md giofont.Font) {
if _, ok := s.faceToIndex[f.Font]; ok { if _, ok := s.faceToIndex[f.Font]; ok {
return return
} }
@@ -349,7 +357,7 @@ func (s *shaperImpl) splitBidi(input shaping.Input) []shaping.Input {
if err != nil { if err != nil {
return []shaping.Input{input} return []shaping.Input{input}
} }
for i := range out.NumRuns() { for i := 0; i < out.NumRuns(); i++ {
currentInput := input currentInput := input
run := out.Run(i) run := out.Run(i)
dir := run.Direction() dir := run.Direction()
@@ -369,11 +377,11 @@ func (s *shaperImpl) splitBidi(input shaping.Input) []shaping.Input {
// ResolveFace allows shaperImpl to implement shaping.FontMap, wrapping its fontMap // ResolveFace allows shaperImpl to implement shaping.FontMap, wrapping its fontMap
// field and ensuring that any faces loaded as part of the search are registered with // field and ensuring that any faces loaded as part of the search are registered with
// ids so that they can be referred to by a GlyphID. // ids so that they can be referred to by a GlyphID.
func (s *shaperImpl) ResolveFace(r rune) *font.Face { func (s *shaperImpl) ResolveFace(r rune) font.Face {
face := s.fontMap.ResolveFace(r) face := s.fontMap.ResolveFace(r)
if face != nil { if face != nil {
family, aspect := s.fontMap.FontMetadata(face.Font) family, aspect := s.fontMap.FontMetadata(face.Font)
md := opentype.DescriptionToFont(font.Description{ md := opentype.DescriptionToFont(metadata.Description{
Family: family, Family: family,
Aspect: aspect, Aspect: aspect,
}) })
@@ -483,11 +491,9 @@ func wrapPolicyToGoText(p WrapPolicy) shaping.LineBreakPolicy {
// shapeAndWrapText invokes the text shaper and returns wrapped lines in the shaper's native format. // shapeAndWrapText invokes the text shaper and returns wrapped lines in the shaper's native format.
func (s *shaperImpl) shapeAndWrapText(params Parameters, txt []rune) (_ []shaping.Line, truncated int) { func (s *shaperImpl) shapeAndWrapText(params Parameters, txt []rune) (_ []shaping.Line, truncated int) {
wc := shaping.WrapConfig{ wc := shaping.WrapConfig{
Direction: mapDirection(params.Locale.Direction), TruncateAfterLines: params.MaxLines,
TruncateAfterLines: params.MaxLines, TextContinues: params.forceTruncate,
TextContinues: params.forceTruncate, BreakPolicy: wrapPolicyToGoText(params.WrapPolicy),
BreakPolicy: wrapPolicyToGoText(params.WrapPolicy),
DisableTrailingWhitespaceTrim: params.DisableSpaceTrim,
} }
families := s.defaultFaces families := s.defaultFaces
if params.Font.Typeface != "" { if params.Font.Typeface != "" {
@@ -657,7 +663,7 @@ func (s *shaperImpl) Shape(pathOps *op.Ops, gs []Glyph) clip.PathSpec {
scaleFactor := fixedToFloat(ppem) / float32(face.Upem()) scaleFactor := fixedToFloat(ppem) / float32(face.Upem())
glyphData := face.GlyphData(gid) glyphData := face.GlyphData(gid)
switch glyphData := glyphData.(type) { switch glyphData := glyphData.(type) {
case font.GlyphOutline: case api.GlyphOutline:
outline := glyphData outline := glyphData
// Move to glyph position. // Move to glyph position.
pos := f32.Point{ pos := f32.Point{
@@ -672,13 +678,13 @@ func (s *shaperImpl) Shape(pathOps *op.Ops, gs []Glyph) clip.PathSpec {
for _, fseg := range outline.Segments { for _, fseg := range outline.Segments {
nargs := 1 nargs := 1
switch fseg.Op { switch fseg.Op {
case gotextot.SegmentOpQuadTo: case api.SegmentOpQuadTo:
nargs = 2 nargs = 2
case gotextot.SegmentOpCubeTo: case api.SegmentOpCubeTo:
nargs = 3 nargs = 3
} }
var args [3]f32.Point var args [3]f32.Point
for i := range nargs { for i := 0; i < nargs; i++ {
a := f32.Point{ a := f32.Point{
X: fseg.Args[i].X * scaleFactor, X: fseg.Args[i].X * scaleFactor,
Y: -fseg.Args[i].Y * scaleFactor, Y: -fseg.Args[i].Y * scaleFactor,
@@ -689,13 +695,13 @@ func (s *shaperImpl) Shape(pathOps *op.Ops, gs []Glyph) clip.PathSpec {
} }
} }
switch fseg.Op { switch fseg.Op {
case gotextot.SegmentOpMoveTo: case api.SegmentOpMoveTo:
builder.Move(args[0]) builder.Move(args[0])
case gotextot.SegmentOpLineTo: case api.SegmentOpLineTo:
builder.Line(args[0]) builder.Line(args[0])
case gotextot.SegmentOpQuadTo: case api.SegmentOpQuadTo:
builder.Quad(args[0], args[1]) builder.Quad(args[0], args[1])
case gotextot.SegmentOpCubeTo: case api.SegmentOpCubeTo:
builder.Cube(args[0], args[1], args[2]) builder.Cube(args[0], args[1], args[2])
default: default:
panic("unsupported segment op") panic("unsupported segment op")
@@ -736,16 +742,16 @@ func (s *shaperImpl) Bitmaps(ops *op.Ops, gs []Glyph) op.CallOp {
} }
glyphData := face.GlyphData(gid) glyphData := face.GlyphData(gid)
switch glyphData := glyphData.(type) { switch glyphData := glyphData.(type) {
case font.GlyphBitmap: case api.GlyphBitmap:
var imgOp paint.ImageOp var imgOp paint.ImageOp
var imgSize image.Point var imgSize image.Point
bitmapData, ok := s.bitmapGlyphCache.Get(g.ID) bitmapData, ok := s.bitmapGlyphCache.Get(g.ID)
if !ok { if !ok {
var img image.Image var img image.Image
switch glyphData.Format { switch glyphData.Format {
case font.PNG, font.JPG, font.TIFF: case api.PNG, api.JPG, api.TIFF:
img, _, _ = image.Decode(bytes.NewReader(glyphData.Data)) img, _, _ = image.Decode(bytes.NewReader(glyphData.Data))
case font.BlackAndWhite: case api.BlackAndWhite:
// This is a complex family of uncompressed bitmaps that don't seem to be // This is a complex family of uncompressed bitmaps that don't seem to be
// very common in practice. We can try adding support later if needed. // very common in practice. We can try adding support later if needed.
fallthrough fallthrough
@@ -760,7 +766,7 @@ func (s *shaperImpl) Bitmaps(ops *op.Ops, gs []Glyph) op.CallOp {
imgOp = bitmapData.img imgOp = bitmapData.img
imgSize = bitmapData.size imgSize = bitmapData.size
} }
off := op.Affine(f32.AffineId().Offset(f32.Point{ off := op.Affine(f32.Affine2D{}.Offset(f32.Point{
X: fixedToFloat((g.X - x) - g.Offset.X), X: fixedToFloat((g.X - x) - g.Offset.X),
Y: fixedToFloat(g.Offset.Y + g.Bounds.Min.Y), Y: fixedToFloat(g.Offset.Y + g.Bounds.Min.Y),
})).Push(ops) })).Push(ops)
@@ -776,7 +782,7 @@ func (s *shaperImpl) Bitmaps(ops *op.Ops, gs []Glyph) op.CallOp {
Y: g.Bounds.Max.Y.Round(), Y: g.Bounds.Max.Y.Round(),
}, },
}.Size() }.Size()
aff := op.Affine(f32.AffineId().Scale(f32.Point{}, f32.Point{ aff := op.Affine(f32.Affine2D{}.Scale(f32.Point{}, f32.Point{
X: float32(glyphSize.X) / float32(imgSize.X), X: float32(glyphSize.X) / float32(imgSize.X),
Y: float32(glyphSize.Y) / float32(imgSize.Y), Y: float32(glyphSize.Y) / float32(imgSize.Y),
})).Push(ops) })).Push(ops)
@@ -801,7 +807,7 @@ type langConfig struct {
} }
// toInput converts its parameters into a shaping.Input. // toInput converts its parameters into a shaping.Input.
func toInput(face *font.Face, ppem fixed.Int26_6, lc langConfig, runes []rune) shaping.Input { func toInput(face font.Face, ppem fixed.Int26_6, lc langConfig, runes []rune) shaping.Input {
var input shaping.Input var input shaping.Input
input.Direction = lc.Direction input.Direction = lc.Direction
input.Text = runes input.Text = runes
@@ -861,14 +867,13 @@ func toGioGlyphs(in []shaping.Glyph, ppem fixed.Int26_6, faceIdx int) []glyph {
} }
// toLine converts the output into a Line with the provided dominant text direction. // toLine converts the output into a Line with the provided dominant text direction.
func toLine(faceToIndex map[*font.Font]int, o shaping.Line, dir system.TextDirection) line { func toLine(faceToIndex map[font.Font]int, o shaping.Line, dir system.TextDirection) line {
if len(o) < 1 { if len(o) < 1 {
return line{} return line{}
} }
line := line{ line := line{
runs: make([]runLayout, len(o)), runs: make([]runLayout, len(o)),
direction: dir, direction: dir,
visualOrder: make([]int, len(o)),
} }
maxSize := fixed.Int26_6(0) maxSize := fixed.Int26_6(0)
for i := range o { for i := range o {
@@ -876,7 +881,7 @@ func toLine(faceToIndex map[*font.Font]int, o shaping.Line, dir system.TextDirec
if run.Size > maxSize { if run.Size > maxSize {
maxSize = run.Size maxSize = run.Size
} }
var font *font.Font var font font.Font
if run.Face != nil { if run.Face != nil {
font = run.Face.Font font = run.Face.Font
} }
@@ -886,13 +891,11 @@ func toLine(faceToIndex map[*font.Font]int, o shaping.Line, dir system.TextDirec
Count: run.Runes.Count, Count: run.Runes.Count,
Offset: line.runeCount, Offset: line.runeCount,
}, },
Direction: unmapDirection(run.Direction), Direction: unmapDirection(run.Direction),
face: run.Face, face: run.Face,
Advance: run.Advance, Advance: run.Advance,
PPEM: run.Size, PPEM: run.Size,
VisualPosition: int(run.VisualIndex),
} }
line.visualOrder[run.VisualIndex] = i
line.runeCount += run.Runes.Count line.runeCount += run.Runes.Count
line.width += run.Advance line.width += run.Advance
if line.ascent < run.LineBounds.Ascent { if line.ascent < run.LineBounds.Ascent {
@@ -903,11 +906,64 @@ func toLine(faceToIndex map[*font.Font]int, o shaping.Line, dir system.TextDirec
} }
} }
line.lineHeight = maxSize line.lineHeight = maxSize
// Iterate and resolve the X of each run. computeVisualOrder(&line)
x := fixed.Int26_6(0)
for _, runIdx := range line.visualOrder {
line.runs[runIdx].X = x
x += line.runs[runIdx].Advance
}
return line return line
} }
// computeVisualOrder will populate the Line's VisualOrder field and the
// VisualPosition field of each element in Runs.
func computeVisualOrder(l *line) {
l.visualOrder = make([]int, len(l.runs))
const none = -1
bidiRangeStart := none
// visPos returns the visual position for an individual logically-indexed
// run in this line, taking only the line's overall text direction into
// account.
visPos := func(logicalIndex int) int {
if l.direction.Progression() == system.TowardOrigin {
return len(l.runs) - 1 - logicalIndex
}
return logicalIndex
}
// resolveBidi populated the line's VisualOrder fields for the elements in the
// half-open range [bidiRangeStart:bidiRangeEnd) indicating that those elements
// should be displayed in reverse-visual order.
resolveBidi := func(bidiRangeStart, bidiRangeEnd int) {
firstVisual := bidiRangeEnd - 1
// Just found the end of a bidi range.
for startIdx := bidiRangeStart; startIdx < bidiRangeEnd; startIdx++ {
pos := visPos(firstVisual)
l.runs[startIdx].VisualPosition = pos
l.visualOrder[pos] = startIdx
firstVisual--
}
bidiRangeStart = none
}
for runIdx, run := range l.runs {
if run.Direction.Progression() != l.direction.Progression() {
if bidiRangeStart == none {
bidiRangeStart = runIdx
}
continue
} else if bidiRangeStart != none {
// Just found the end of a bidi range.
resolveBidi(bidiRangeStart, runIdx)
bidiRangeStart = none
}
pos := visPos(runIdx)
l.runs[runIdx].VisualPosition = pos
l.visualOrder[pos] = runIdx
}
if bidiRangeStart != none {
// We ended iteration within a bidi segment, resolve it.
resolveBidi(bidiRangeStart, len(l.runs))
}
// Iterate and resolve the X of each run.
x := fixed.Int26_6(0)
for _, runIdx := range l.visualOrder {
l.runs[runIdx].X = x
x += l.runs[runIdx].Advance
}
}
+119 -3
View File
@@ -3,13 +3,14 @@ package text
import ( import (
"fmt" "fmt"
"math" "math"
"slices" "reflect"
"strconv" "strconv"
"testing" "testing"
nsareg "eliasnaur.com/font/noto/sans/arabic/regular" nsareg "eliasnaur.com/font/noto/sans/arabic/regular"
"github.com/go-text/typesetting/font" "github.com/go-text/typesetting/font"
"github.com/go-text/typesetting/shaping" "github.com/go-text/typesetting/shaping"
"golang.org/x/exp/slices"
"golang.org/x/image/font/gofont/goregular" "golang.org/x/image/font/gofont/goregular"
"golang.org/x/image/math/fixed" "golang.org/x/image/math/fixed"
@@ -126,8 +127,7 @@ func TestShapingAlignWidth(t *testing.T) {
}, },
} { } {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
lines := shaper.LayoutString(Parameters{ lines := shaper.LayoutString(Parameters{PxPerEm: ppem,
PxPerEm: ppem,
MinWidth: tc.minWidth, MinWidth: tc.minWidth,
MaxWidth: tc.maxWidth, MaxWidth: tc.maxWidth,
Locale: english, Locale: english,
@@ -175,6 +175,7 @@ func TestNewlineSynthesis(t *testing.T) {
}, },
} { } {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
doc := shaper.LayoutRunes(Parameters{ doc := shaper.LayoutRunes(Parameters{
PxPerEm: ppem, PxPerEm: ppem,
MaxWidth: 200, MaxWidth: 200,
@@ -207,6 +208,7 @@ func TestNewlineSynthesis(t *testing.T) {
} }
}) })
} }
} }
// simpleGlyph returns a simple square glyph with the provided cluster // simpleGlyph returns a simple square glyph with the provided cluster
@@ -448,6 +450,120 @@ func TestToLine(t *testing.T) {
} }
} }
func TestComputeVisualOrder(t *testing.T) {
type testcase struct {
name string
input line
expectedVisualOrder []int
}
for _, tc := range []testcase{
{
name: "ltr",
input: line{
direction: system.LTR,
runs: []runLayout{
{Direction: system.LTR},
{Direction: system.LTR},
{Direction: system.LTR},
},
},
expectedVisualOrder: []int{0, 1, 2},
},
{
name: "rtl",
input: line{
direction: system.RTL,
runs: []runLayout{
{Direction: system.RTL},
{Direction: system.RTL},
{Direction: system.RTL},
},
},
expectedVisualOrder: []int{2, 1, 0},
},
{
name: "bidi-ltr",
input: line{
direction: system.LTR,
runs: []runLayout{
{Direction: system.LTR},
{Direction: system.RTL},
{Direction: system.RTL},
{Direction: system.RTL},
{Direction: system.LTR},
},
},
expectedVisualOrder: []int{0, 3, 2, 1, 4},
},
{
name: "bidi-ltr-complex",
input: line{
direction: system.LTR,
runs: []runLayout{
{Direction: system.RTL},
{Direction: system.RTL},
{Direction: system.LTR},
{Direction: system.RTL},
{Direction: system.RTL},
{Direction: system.LTR},
{Direction: system.RTL},
{Direction: system.RTL},
{Direction: system.LTR},
{Direction: system.RTL},
{Direction: system.RTL},
},
},
expectedVisualOrder: []int{1, 0, 2, 4, 3, 5, 7, 6, 8, 10, 9},
},
{
name: "bidi-rtl",
input: line{
direction: system.RTL,
runs: []runLayout{
{Direction: system.RTL},
{Direction: system.LTR},
{Direction: system.LTR},
{Direction: system.LTR},
{Direction: system.RTL},
},
},
expectedVisualOrder: []int{4, 1, 2, 3, 0},
},
{
name: "bidi-rtl-complex",
input: line{
direction: system.RTL,
runs: []runLayout{
{Direction: system.LTR},
{Direction: system.LTR},
{Direction: system.RTL},
{Direction: system.LTR},
{Direction: system.LTR},
{Direction: system.RTL},
{Direction: system.LTR},
{Direction: system.LTR},
{Direction: system.RTL},
{Direction: system.LTR},
{Direction: system.LTR},
},
},
expectedVisualOrder: []int{9, 10, 8, 6, 7, 5, 3, 4, 2, 0, 1},
},
} {
t.Run(tc.name, func(t *testing.T) {
computeVisualOrder(&tc.input)
if !reflect.DeepEqual(tc.input.visualOrder, tc.expectedVisualOrder) {
t.Errorf("expected visual order %v, got %v", tc.expectedVisualOrder, tc.input.visualOrder)
}
for i, visualIndex := range tc.input.visualOrder {
if pos := tc.input.runs[visualIndex].VisualPosition; pos != i {
t.Errorf("line.VisualOrder[%d]=%d, but line.Runs[%d].VisualPosition=%d", i, visualIndex, visualIndex, pos)
}
}
})
}
}
func FuzzLayout(f *testing.F) { func FuzzLayout(f *testing.F) {
ltrFace, _ := opentype.Parse(goregular.TTF) ltrFace, _ := opentype.Parse(goregular.TTF)
rtlFace, _ := opentype.Parse(nsareg.TTF) rtlFace, _ := opentype.Parse(nsareg.TTF)
+2 -2
View File
@@ -35,10 +35,10 @@ func TestPathLRU(t *testing.T) {
} }
func testLRU(t *testing.T, put func(i int), get func(i int) bool) { func testLRU(t *testing.T, put func(i int), get func(i int) bool) {
for i := range maxSize { for i := 0; i < maxSize; i++ {
put(i) put(i)
} }
for i := range maxSize { for i := 0; i < maxSize; i++ {
if !get(i) { if !get(i) {
t.Fatalf("key %d was evicted", i) t.Fatalf("key %d was evicted", i)
} }
+2 -15
View File
@@ -76,11 +76,6 @@ type Parameters struct {
// text with a MaxLines. It is unexported because this behavior only makes sense for the // text with a MaxLines. It is unexported because this behavior only makes sense for the
// shaper to control when it iterates paragraphs of text. // shaper to control when it iterates paragraphs of text.
forceTruncate bool forceTruncate bool
// DisableSpaceTrim prevents the width of the final whitespace glyph on a line from being zeroed.
// This is desirable for text editors (so that the whitespace can be selected), but is undesirable
// for ordinary display text.
DisableSpaceTrim bool
} }
type FontFace = giofont.FontFace type FontFace = giofont.FontFace
@@ -204,15 +199,7 @@ func (f Flags) String() string {
type GlyphID uint64 type GlyphID uint64
// Shaper converts strings of text into glyphs that can be displayed. The same // Shaper converts strings of text into glyphs that can be displayed.
// Shaper should not be used in different goroutines.
//
// The Shaper controls text layout and has a cache, implemented as a map, and
// so laying out text in two different goroutines can easily result in
// concurrent access to said map, resulting in a panic.
//
// Practically speaking, this means you should use different Shapers for
// different top-level windows.
type Shaper struct { type Shaper struct {
config struct { config struct {
disableSystemFonts bool disableSystemFonts bool
@@ -544,7 +531,7 @@ func (l *Shaper) NextGlyph() (_ Glyph, ok bool) {
// of a valid cursor position they can use for "after" such a newline, // of a valid cursor position they can use for "after" such a newline,
// taking text alignment into account. // taking text alignment into account.
l.pararagraphStart.X = l.txt.alignment.Align(line.direction, 0, l.txt.alignWidth) l.pararagraphStart.X = l.txt.alignment.Align(line.direction, 0, l.txt.alignWidth)
l.pararagraphStart.Y = glyph.Y + int32(line.lineHeight.Round()) l.pararagraphStart.Y = glyph.Y + int32((glyph.Ascent + glyph.Descent).Ceil())
} }
} }
return glyph, true return glyph, true
+1 -1
View File
@@ -2,7 +2,6 @@ package text
import ( import (
"fmt" "fmt"
"slices"
"strings" "strings"
"testing" "testing"
@@ -11,6 +10,7 @@ import (
"gioui.org/font/gofont" "gioui.org/font/gofont"
"gioui.org/font/opentype" "gioui.org/font/opentype"
"gioui.org/io/system" "gioui.org/io/system"
"golang.org/x/exp/slices"
"golang.org/x/image/font/gofont/goregular" "golang.org/x/image/font/gofont/goregular"
"golang.org/x/image/math/fixed" "golang.org/x/image/math/fixed"
) )
+4
View File
@@ -9,6 +9,7 @@ import (
"gioui.org/gesture" "gioui.org/gesture"
"gioui.org/io/event" "gioui.org/io/event"
"gioui.org/io/key" "gioui.org/io/key"
"gioui.org/io/pointer"
"gioui.org/io/semantic" "gioui.org/io/semantic"
"gioui.org/layout" "gioui.org/layout"
"gioui.org/op" "gioui.org/op"
@@ -140,6 +141,9 @@ func (b *Clickable) update(t event.Tag, gtx layout.Context) (Click, bool) {
} }
} }
case gesture.KindPress: case gesture.KindPress:
if e.Source == pointer.Mouse {
gtx.Execute(key.FocusCmd{Tag: t})
}
b.history = append(b.history, Press{ b.history = append(b.history, Press{
Position: e.Position, Position: e.Position,
Start: gtx.Now, Start: gtx.Now,
+23 -6
View File
@@ -11,11 +11,8 @@ import (
// Decorations handles the states of window decorations. // Decorations handles the states of window decorations.
type Decorations struct { type Decorations struct {
// Maximized controls the look and behaviour of the maximize
// button. It is the user's responsibility to set Maximized
// according to the window state reported through [app.ConfigEvent].
Maximized bool
clicks map[int]*Clickable clicks map[int]*Clickable
maximized bool
} }
// LayoutMove lays out the widget that makes a window movable. // LayoutMove lays out the widget that makes a window movable.
@@ -43,6 +40,17 @@ func (d *Decorations) Clickable(action system.Action) *Clickable {
return click return click
} }
// Perform updates the decorations as if the specified actions were
// performed by the user.
func (d *Decorations) Perform(actions system.Action) {
if actions&system.ActionMaximize != 0 {
d.maximized = true
}
if actions&(system.ActionUnmaximize|system.ActionMinimize|system.ActionFullscreen) != 0 {
d.maximized = false
}
}
// Update the state and return the set of actions activated by the user. // Update the state and return the set of actions activated by the user.
func (d *Decorations) Update(gtx layout.Context) system.Action { func (d *Decorations) Update(gtx layout.Context) system.Action {
var actions system.Action var actions system.Action
@@ -52,12 +60,21 @@ func (d *Decorations) Update(gtx layout.Context) system.Action {
} }
action := system.Action(1 << idx) action := system.Action(1 << idx)
switch { switch {
case action == system.ActionMaximize && d.Maximized: case action == system.ActionMaximize && d.maximized:
action = system.ActionUnmaximize action = system.ActionUnmaximize
case action == system.ActionUnmaximize && !d.Maximized: case action == system.ActionUnmaximize && !d.maximized:
action = system.ActionMaximize action = system.ActionMaximize
} }
switch action {
case system.ActionMaximize, system.ActionUnmaximize:
d.maximized = !d.maximized
}
actions |= action actions |= action
} }
return actions return actions
} }
// Maximized returns whether the window is maximized.
func (d *Decorations) Maximized() bool {
return d.maximized
}
+16 -3
View File
@@ -610,7 +610,6 @@ func (e *Editor) initBuffer() {
e.text.SingleLine = e.SingleLine e.text.SingleLine = e.SingleLine
e.text.Mask = e.Mask e.text.Mask = e.Mask
e.text.WrapPolicy = e.WrapPolicy e.text.WrapPolicy = e.WrapPolicy
e.text.DisableSpaceTrim = true
} }
// Update the state of the editor in response to input events. Update consumes editor // Update the state of the editor in response to input events. Update consumes editor
@@ -929,7 +928,7 @@ func (e *Editor) replace(start, end int, s string, addHistory bool) int {
if addHistory { if addHistory {
deleted := make([]rune, 0, replaceSize) deleted := make([]rune, 0, replaceSize)
readPos := e.text.ByteOffset(start) readPos := e.text.ByteOffset(start)
for range replaceSize { for i := 0; i < replaceSize; i++ {
ru, s, _ := e.text.ReadRuneAt(int64(readPos)) ru, s, _ := e.text.ReadRuneAt(int64(readPos))
readPos += int64(s) readPos += int64(s)
deleted = append(deleted, ru) deleted = append(deleted, ru)
@@ -1021,7 +1020,7 @@ func (e *Editor) deleteWord(distance int) (deletedRunes int) {
return r return r
} }
runes := 1 runes := 1
for range words { for ii := 0; ii < words; ii++ {
r := next(runes) r := next(runes)
wantSpace := unicode.IsSpace(r) wantSpace := unicode.IsSpace(r)
for r := next(runes); unicode.IsSpace(r) == wantSpace && !atEnd(runes); r = next(runes) { for r := next(runes); unicode.IsSpace(r) == wantSpace && !atEnd(runes); r = next(runes) {
@@ -1093,6 +1092,20 @@ func (e *Editor) Regions(start, end int, regions []Region) []Region {
return e.text.Regions(start, end, regions) return e.text.Regions(start, end, regions)
} }
func max(a, b int) int {
if a > b {
return a
}
return b
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
func abs(n int) int { func abs(n int) int {
if n < 0 { if n < 0 {
return -n return -n
+2 -2
View File
@@ -992,7 +992,7 @@ func TestEditorSelectShortcuts(t *testing.T) {
tFont := font.Font{} tFont := font.Font{}
tFontSize := unit.Sp(10) tFontSize := unit.Sp(10)
tShaper := text.NewShaper(text.NoSystemFonts(), text.WithCollection(gofont.Collection())) tShaper := text.NewShaper(text.NoSystemFonts(), text.WithCollection(gofont.Collection()))
tEditor := &Editor{ var tEditor = &Editor{
SingleLine: false, SingleLine: false,
ReadOnly: true, ReadOnly: true,
} }
@@ -1255,7 +1255,7 @@ func TestNoFilterAllocs(t *testing.T) {
} }
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
for b.Loop() { for i := 0; i < b.N; i++ {
e.Update(gtx) e.Update(gtx)
} }
}) })
+3 -3
View File
@@ -39,7 +39,7 @@ func (fit Fit) scale(cs layout.Constraints, pos layout.Direction, dims layout.Di
offset := pos.Position(widgetSize, dims.Size) offset := pos.Position(widgetSize, dims.Size)
dims.Baseline += offset.Y dims.Baseline += offset.Y
return dims, f32.AffineId().Offset(layout.FPt(offset)) return dims, f32.Affine2D{}.Offset(layout.FPt(offset))
} }
scale := f32.Point{ scale := f32.Point{
@@ -73,7 +73,7 @@ func (fit Fit) scale(cs layout.Constraints, pos layout.Direction, dims layout.Di
offset := pos.Position(widgetSize, dims.Size) offset := pos.Position(widgetSize, dims.Size)
dims.Baseline += offset.Y dims.Baseline += offset.Y
return dims, f32.AffineId().Offset(layout.FPt(offset)) return dims, f32.Affine2D{}.Offset(layout.FPt(offset))
} }
case Fill: case Fill:
} }
@@ -85,7 +85,7 @@ func (fit Fit) scale(cs layout.Constraints, pos layout.Direction, dims layout.Di
dims.Baseline = int(float32(dims.Baseline) * scale.Y) dims.Baseline = int(float32(dims.Baseline) * scale.Y)
offset := pos.Position(scaledSize, dims.Size) offset := pos.Position(scaledSize, dims.Size)
trans := f32.AffineId(). trans := f32.Affine2D{}.
Scale(f32.Point{}, scale). Scale(f32.Point{}, scale).
Offset(layout.FPt(offset)) Offset(layout.FPt(offset))

Some files were not shown because too many files have changed in this diff Show More