Compare commits

..

7 Commits

Author SHA1 Message Date
Chris Waldon 718be79d9e app: fix automatic window decoration action processing
This commit adapts the use of the automatic window decorations to the
event processing changes introduced in v0.4.0. You must update widget
state before laying it out, not after. Doing so after (as this code used
to do) results in discarding updates.

Fixes: https://todo.sr.ht/~eliasnaur/gio/542
Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2024-01-08 08:18:09 -05:00
Dominik Honnef 0073e1a167 widget: don't refer to non-existent method Clickable.Clicks
Signed-off-by: Dominik Honnef <dominik@honnef.co>
2024-01-08 08:18:09 -05:00
Dominik Honnef 32ecec5538 gesture: adjust ClickKind.String for ClickType -> ClickKind rename
Signed-off-by: Dominik Honnef <dominik@honnef.co>
2024-01-08 08:18:09 -05:00
Elias Naur 6eb33b8a56 app: [Windows] tolerate gpu.ErrDeviceLost from Refresh
Fixes: https://todo.sr.ht/~eliasnaur/gio/552
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2024-01-08 08:18:09 -05:00
Elias Naur 4617526e12 app: don't route internal wakeup events to the Router
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2024-01-08 08:18:09 -05:00
Elias Naur dbc7a900bd app: [Windows] fix restore size when leaving fullscreen
Signed-off-by: Elias Naur <mail@eliasnaur.com>
2024-01-08 08:18:09 -05:00
Chris Waldon fb3ae95b28 widget/material: fix list scrollbar display
This commit fixes a visual misalignment in scrollbars resulting from subtle differences
in the semantics of layout.Stack and layout.Background. layout.Stack will position expanded
children according to their minimum constraint regardless of their returned size, whereas
layout.Background uses their returned size. This means that layout.Expanded widgets returning
zero dimensions are positioned correctly, but they break when converted to use layout.Background.

This commit fixes the problem by returning correct dimensions from the scrollbar track.

Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
2024-01-08 08:18:09 -05:00
183 changed files with 9512 additions and 9854 deletions
+3 -3
View File
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: Unlicense OR MIT # SPDX-License-Identifier: Unlicense OR MIT
image: debian/stable image: debian/testing
packages: packages:
- clang - clang
- cmake - cmake
@@ -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.19.11.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.19.11.freebsd-amd64.tar.gz | tar -C /home/build/sdk -xzf -
- test_gio: | - test_gio: |
cd gio cd gio
go test ./... go test ./...
+3 -11
View File
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: Unlicense OR MIT # SPDX-License-Identifier: Unlicense OR MIT
image: debian/stable image: debian/testing
packages: packages:
- curl - curl
- pkg-config - pkg-config
@@ -18,12 +18,6 @@ packages:
- libxinerama-dev - libxinerama-dev
- libxi-dev - libxi-dev
- libxxf86vm-dev - libxxf86vm-dev
- libegl-mesa0
- libglx-mesa0
- libgl1-mesa-dri
- mesa-libgallium
- libgbm1
- libegl1
- mesa-vulkan-drivers - mesa-vulkan-drivers
- wine - wine
- xvfb - xvfb
@@ -46,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.19.11.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 .)"
@@ -66,7 +60,7 @@ tasks:
- add_32bit_arch: | - add_32bit_arch: |
sudo dpkg --add-architecture i386 sudo dpkg --add-architecture i386
sudo apt-get update sudo apt-get update
sudo apt-get install -y "libwayland-dev:i386" "libx11-dev:i386" "libx11-xcb-dev:i386" "libxkbcommon-dev:i386" "libxkbcommon-x11-dev:i386" "libgles2-mesa-dev:i386" "libegl1-mesa-dev:i386" "libffi-dev:i386" "libvulkan-dev:i386" "libxcursor-dev:i386" "libegl-mesa0:i386" "libglx-mesa0:i386" "libgbm1:i386" "mesa-libgallium:i386" "libgl1-mesa-dri:i386" sudo apt-get install -y "libwayland-dev:i386" "libx11-dev:i386" "libx11-xcb-dev:i386" "libxkbcommon-dev:i386" "libxkbcommon-x11-dev:i386" "libgles2-mesa-dev:i386" "libegl1-mesa-dev:i386" "libffi-dev:i386" "libvulkan-dev:i386" "libxcursor-dev:i386"
- test_gio: | - test_gio: |
cd gio cd gio
go test -race ./... go test -race ./...
@@ -86,8 +80,6 @@ tasks:
unzip -q ndk.zip unzip -q ndk.zip
rm ndk.zip rm ndk.zip
mv android-ndk-* ndk-bundle mv android-ndk-* ndk-bundle
# sdkmanager needs lots of file descriptors
ulimit -n 10000
yes|sdkmanager --licenses yes|sdkmanager --licenses
sdkmanager "platforms;android-31" "build-tools;32.0.0" sdkmanager "platforms;android-31" "build-tools;32.0.0"
- test_android: | - test_android: |
+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.19.11.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: |
-17
View File
@@ -4,7 +4,6 @@ package org.gioui;
import android.app.Activity; import android.app.Activity;
import android.os.Bundle; import android.os.Bundle;
import android.content.Intent;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.View; import android.view.View;
@@ -30,7 +29,6 @@ public final class GioActivity extends Activity {
layer.addView(view); layer.addView(view);
setContentView(layer); setContentView(layer);
onNewIntent(this.getIntent());
} }
@Override public void onDestroy() { @Override public void onDestroy() {
@@ -48,16 +46,6 @@ public final class GioActivity extends Activity {
super.onStop(); super.onStop();
} }
@Override public void onPause() {
super.onPause();
view.pause();
}
@Override public void onResume() {
super.onResume();
view.resume();
}
@Override public void onConfigurationChanged(Configuration c) { @Override public void onConfigurationChanged(Configuration c) {
super.onConfigurationChanged(c); super.onConfigurationChanged(c);
view.configurationChanged(); view.configurationChanged();
@@ -72,9 +60,4 @@ public final class GioActivity extends Activity {
if (!view.backPressed()) if (!view.backPressed())
super.onBackPressed(); super.onBackPressed();
} }
@Override protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
view.onIntentEvent(intent);
}
} }
+8 -28
View File
@@ -12,7 +12,6 @@ import android.app.Fragment;
import android.app.FragmentManager; import android.app.FragmentManager;
import android.app.FragmentTransaction; import android.app.FragmentTransaction;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Matrix; import android.graphics.Matrix;
@@ -62,11 +61,12 @@ public final class GioView extends SurfaceView implements Choreographer.FrameCal
private static boolean jniLoaded; private static boolean jniLoaded;
private final SurfaceHolder.Callback surfCallbacks; private final SurfaceHolder.Callback surfCallbacks;
private final View.OnFocusChangeListener focusCallback;
private final InputMethodManager imm; private final InputMethodManager imm;
private final float scrollXScale; private final float scrollXScale;
private final float scrollYScale; private final float scrollYScale;
private final AccessibilityManager accessManager;
private int keyboardHint; private int keyboardHint;
private AccessibilityManager accessManager;
private long nhandle; private long nhandle;
@@ -113,6 +113,12 @@ public final class GioView extends SurfaceView implements Choreographer.FrameCal
nhandle = onCreateView(this); nhandle = onCreateView(this);
setFocusable(true); setFocusable(true);
setFocusableInTouchMode(true); setFocusableInTouchMode(true);
focusCallback = new View.OnFocusChangeListener() {
@Override public void onFocusChange(View v, boolean focus) {
GioView.this.onFocusChange(nhandle, focus);
}
};
setOnFocusChangeListener(focusCallback);
surfCallbacks = new SurfaceHolder.Callback() { surfCallbacks = new SurfaceHolder.Callback() {
@Override public void surfaceCreated(SurfaceHolder holder) { @Override public void surfaceCreated(SurfaceHolder holder) {
// Ignore; surfaceChanged is guaranteed to be called immediately after this. // Ignore; surfaceChanged is guaranteed to be called immediately after this.
@@ -253,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();
@@ -309,15 +311,6 @@ public final class GioView extends SurfaceView implements Choreographer.FrameCal
window.setAttributes(layoutParams); window.setAttributes(layoutParams);
} }
protected void onIntentEvent(Intent intent) {
if (intent == null) {
return;
}
if (intent.getData() != null) {
this.onOpenURI(nhandle, intent.getData().toString());
}
}
@Override protected boolean dispatchHoverEvent(MotionEvent event) { @Override protected boolean dispatchHoverEvent(MotionEvent event) {
if (!accessManager.isTouchExplorationEnabled()) { if (!accessManager.isTouchExplorationEnabled()) {
return super.dispatchHoverEvent(event); return super.dispatchHoverEvent(event);
@@ -475,18 +468,6 @@ public final class GioView extends SurfaceView implements Choreographer.FrameCal
} }
} }
public void pause() {
if (nhandle != 0) {
onFocusChange(nhandle, false);
}
}
public void resume() {
if (nhandle != 0) {
onFocusChange(nhandle, true);
}
}
public void destroy() { public void destroy() {
if (nhandle != 0) { if (nhandle != 0) {
onDestroyView(nhandle); onDestroyView(nhandle);
@@ -568,7 +549,6 @@ public final class GioView extends SurfaceView implements Choreographer.FrameCal
static private native void onExitTouchExploration(long handle); static private native void onExitTouchExploration(long handle);
static private native void onA11yFocus(long handle, int viewId); static private native void onA11yFocus(long handle, int viewId);
static private native void onClearA11yFocus(long handle, int viewId); static private native void onClearA11yFocus(long handle, int viewId);
static private native void onOpenURI(long handle, String uri);
static private native void imeSetSnippet(long handle, int start, int end); static private native void imeSetSnippet(long handle, int start, int end);
static private native String imeSnippet(long handle); static private native String imeSnippet(long handle);
static private native int imeSnippetStart(long handle); static private native int imeSnippetStart(long handle);
+11 -145
View File
@@ -3,19 +3,9 @@
package app package app
import ( import (
"gioui.org/io/event"
"golang.org/x/net/idna"
"image"
"net/url"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"time"
"gioui.org/io/input"
"gioui.org/layout"
"gioui.org/op"
"gioui.org/unit"
) )
// extraArgs contains extra arguments to append to // extraArgs contains extra arguments to append to
@@ -30,97 +20,23 @@ var extraArgs string
// On Android ID is the package property of AndroidManifest.xml, // On Android ID is the package property of AndroidManifest.xml,
// on iOS ID is the CFBundleIdentifier of the app Info.plist, // on iOS ID is the CFBundleIdentifier of the app Info.plist,
// on Wayland it is the toplevel app_id, // on Wayland it is the toplevel app_id,
// on X11 it is the X11 XClassHint. // on X11 it is the X11 XClassHint
// //
// ID is set by the [gioui.org/cmd/gogio] tool or manually with the -X linker flag. For example, // ID is set by the gogio tool or manually with the -X linker flag. For example,
// //
// go build -ldflags="-X 'gioui.org/app.ID=org.gioui.example.Kitchen'" . // go build -ldflags="-X 'gioui.org/app.ID=org.gioui.example.Kitchen'" .
// //
// Note that ID is treated as a constant, and that changing it at runtime // Note that ID is treated as a constant, and that changing it at runtime
// is not supported. The default value of ID is filepath.Base(os.Args[0]). // is not supported. Default value of ID is filepath.Base(os.Args[0]).
var ID = "" var ID = ""
// A FrameEvent requests a new frame in the form of a list of func init() {
// operations that describes the window content. if extraArgs != "" {
type FrameEvent struct { args := strings.Split(extraArgs, "|")
// Now is the current animation. Use Now instead of time.Now to os.Args = append(os.Args, args...)
// synchronize animation and to avoid the time.Now call overhead.
Now time.Time
// Metric converts device independent dp and sp to device pixels.
Metric unit.Metric
// Size is the dimensions of the window.
Size image.Point
// Insets represent the space occupied by system decorations and controls.
Insets Insets
// Frame completes the FrameEvent by drawing the graphical operations
// from ops into the window.
Frame func(frame *op.Ops)
// Source is the interface between the window and widgets.
Source input.Source
}
// URLEvent is generated for external requests to open a URL. Unlike window specific events,
// it is delivered through the [Events] iterator.
//
// In order to receive URLEvents the program must register one or more URL schemes. A scheme can
// be registered using gogio, with the `-schemes` flag.
type URLEvent struct {
URL *url.URL
}
// ViewEvent provides handles to the underlying window objects for the
// current display protocol.
type ViewEvent interface {
implementsViewEvent()
ImplementsEvent()
// Valid will return true when the ViewEvent does contains valid handles.
// If a window receives an invalid ViewEvent, it should deinitialize any
// state referring to handles from a previous ViewEvent.
Valid() bool
}
// Insets is the space taken up by
// system decoration such as translucent
// system bars and software keyboards.
type Insets struct {
// Values are in pixels.
Top, Bottom, Left, Right unit.Dp
}
// NewContext is shorthand for
//
// layout.Context{
// Ops: ops,
// Now: e.Now,
// Source: e.Source,
// Metric: e.Metric,
// Constraints: layout.Exact(e.Size),
// }
//
// NewContext calls ops.Reset and adjusts ops for e.Insets.
func NewContext(ops *op.Ops, e FrameEvent) layout.Context {
ops.Reset()
size := e.Size
if e.Insets != (Insets{}) {
left := e.Metric.Dp(e.Insets.Left)
top := e.Metric.Dp(e.Insets.Top)
op.Offset(image.Point{
X: left,
Y: top,
}).Add(ops)
size.X -= left + e.Metric.Dp(e.Insets.Right)
size.Y -= top + e.Metric.Dp(e.Insets.Bottom)
} }
if ID == "" {
return layout.Context{ ID = filepath.Base(os.Args[0])
Ops: ops,
Now: e.Now,
Source: e.Source,
Metric: e.Metric,
Constraints: layout.Exact(size),
} }
} }
@@ -130,7 +46,8 @@ func NewContext(ops *op.Ops, e FrameEvent) layout.Context {
// On iOS NSDocumentDirectory is queried. // On iOS NSDocumentDirectory is queried.
// For Android Context.getFilesDir is used. // For Android Context.getFilesDir is used.
// //
// BUG: On Android, DataDir panics if called before main. // BUG: DataDir blocks on Android until init functions
// have completed.
func DataDir() (string, error) { func DataDir() (string, error) {
return dataDir() return dataDir()
} }
@@ -146,54 +63,3 @@ func DataDir() (string, error) {
func Main() { func Main() {
osMain() osMain()
} }
// Events is an iterator that yields events that are not specific to any window,
// such as [URLEvent]. It never returns.
//
// Events must be called by the main goroutine, and replaces the
// call to [Main].
func Events(yield func(event.Event) bool) {
yieldGlobalEvent = yield
osMain()
}
var yieldGlobalEvent func(evt event.Event) bool
func processGlobalEvent(evt event.Event) {
if yieldGlobalEvent == nil {
return
}
if !yieldGlobalEvent(evt) {
yieldGlobalEvent = nil
}
}
func (FrameEvent) ImplementsEvent() {}
func (URLEvent) ImplementsEvent() {}
func init() {
if extraArgs != "" {
args := strings.Split(extraArgs, "|")
os.Args = append(os.Args, args...)
}
if ID == "" {
ID = filepath.Base(os.Args[0])
}
}
// newURLEvent creates a URLEvent from a raw URL string, handling Punycode decoding.
func newURLEvent(rawurl string) (URLEvent, error) {
u, err := url.Parse(rawurl)
if err != nil {
return URLEvent{}, err
}
u.Host, err = idna.Punycode.ToUnicode(u.Hostname())
if err != nil {
return URLEvent{}, err
}
u, err = url.Parse(u.String())
if err != nil {
return URLEvent{}, err
}
return URLEvent{URL: u}, nil
}
+1
View File
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: Unlicense OR MIT // SPDX-License-Identifier: Unlicense OR MIT
//go:build !android //go:build !android
// +build !android
package app package app
+17 -15
View File
@@ -8,20 +8,21 @@ See https://gioui.org for instructions to set up and run Gio programs.
# Windows # Windows
A Window is run by calling its Event method in a loop. The first time a Create a new Window by calling NewWindow. On mobile platforms or when Gio
method on Window is called, a new GUI window is created and shown. On mobile is embedded in another project, NewWindow merely connects with a previously
platforms or when Gio is embedded in another project, Window merely connects created window.
with a previously created GUI window.
The most important event is [FrameEvent] that prompts an update of the window A Window is run by calling NextEvent in a loop. The most important event is
contents. FrameEvent that prompts an update of the window contents.
For example: For example:
w := new(app.Window) import "gioui.org/unit"
w := app.NewWindow()
for { for {
e := w.Event() e := w.NextEvent()
if e, ok := e.(app.FrameEvent); ok { if e, ok := e.(system.FrameEvent); ok {
ops.Reset() ops.Reset()
// Add operations to ops. // Add operations to ops.
... ...
@@ -31,7 +32,7 @@ For example:
} }
A program must keep receiving events from the event channel until A program must keep receiving events from the event channel until
[DestroyEvent] is received. DestroyEvent is received.
# Main # Main
@@ -48,18 +49,19 @@ For example, to display a blank but otherwise functional window:
func main() { func main() {
go func() { go func() {
w := new(app.Window) w := app.NewWindow()
for { for {
w.Event() w.NextEvent()
} }
}() }()
app.Main() app.Main()
} }
# Events # Event queue
The [Events] iterator yields app-specific events such as [URLEvent]. [Window.Event] A FrameEvent's Queue method returns an event.Queue implementation that distributes
yields events that target a particular window. incoming events to the event handlers declared in the last frame.
See the gioui.org/io/event package for more information about event handlers.
# Permissions # Permissions
+6 -4
View File
@@ -17,8 +17,9 @@ import (
) )
type androidContext struct { type androidContext struct {
win *window win *window
eglSurf egl.NativeWindowType eglSurf egl.NativeWindowType
width, height int
*egl.Context *egl.Context
} }
@@ -44,8 +45,9 @@ func (c *androidContext) Refresh() error {
if err := c.win.setVisual(c.Context.VisualID()); err != nil { if err := c.win.setVisual(c.Context.VisualID()); err != nil {
return err return err
} }
win, _, _ := c.win.nativeWindow() win, width, height := c.win.nativeWindow()
c.eglSurf = egl.NativeWindowType(unsafe.Pointer(win)) c.eglSurf = egl.NativeWindowType(unsafe.Pointer(win))
c.width, c.height = width, height
return nil return nil
} }
@@ -53,7 +55,7 @@ func (c *androidContext) Lock() error {
// The Android emulator creates a broken surface if it is not // The Android emulator creates a broken surface if it is not
// created on the same thread as the context is made current. // created on the same thread as the context is made current.
if c.eglSurf != nil { if c.eglSurf != nil {
if err := c.Context.CreateSurface(c.eglSurf); err != nil { if err := c.Context.CreateSurface(c.eglSurf, c.width, c.height); err != nil {
return err return err
} }
c.eglSurf = nil c.eglSurf = nil
+13 -20
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,12 +54,22 @@ 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))
return nil if eglWin == nil {
return errors.New("wayland: wl_egl_window_create failed")
}
c.eglWin = eglWin
eglSurf := egl.NativeWindowType(uintptr(unsafe.Pointer(eglWin)))
return c.Context.CreateSurface(eglSurf, width, height)
} }
func (c *wlContext) Lock() error { func (c *wlContext) Lock() error {
+17 -12
View File
@@ -5,6 +5,8 @@
package app package app
import ( import (
"golang.org/x/sys/windows"
"gioui.org/internal/egl" "gioui.org/internal/egl"
) )
@@ -22,18 +24,6 @@ func init() {
if err != nil { if err != nil {
return nil, err return nil, err
} }
win, _, _ := w.HWND()
eglSurf := egl.NativeWindowType(win)
if err := ctx.CreateSurface(eglSurf); err != nil {
ctx.Release()
return nil, err
}
if err := ctx.MakeCurrent(); err != nil {
ctx.Release()
return nil, err
}
defer ctx.ReleaseCurrent()
ctx.EnableVSync(true)
return &glContext{win: w, Context: ctx}, nil return &glContext{win: w, Context: ctx}, nil
}, },
}) })
@@ -47,6 +37,21 @@ func (c *glContext) Release() {
} }
func (c *glContext) Refresh() error { func (c *glContext) Refresh() error {
c.Context.ReleaseSurface()
var (
win windows.Handle
width, height int
)
win, width, height = c.win.HWND()
eglSurf := egl.NativeWindowType(win)
if err := c.Context.CreateSurface(eglSurf, width, height); err != nil {
return err
}
if err := c.Context.MakeCurrent(); err != nil {
return err
}
c.Context.EnableVSync(true)
c.Context.ReleaseCurrent()
return nil return nil
} }
+11 -12
View File
@@ -25,18 +25,6 @@ func init() {
if err != nil { if err != nil {
return nil, err return nil, err
} }
win, _, _ := w.window()
eglSurf := egl.NativeWindowType(uintptr(win))
if err := ctx.CreateSurface(eglSurf); err != nil {
ctx.Release()
return nil, err
}
if err := ctx.MakeCurrent(); err != nil {
ctx.Release()
return nil, err
}
defer ctx.ReleaseCurrent()
ctx.EnableVSync(true)
return &x11Context{win: w, Context: ctx}, nil return &x11Context{win: w, Context: ctx}, nil
} }
} }
@@ -49,6 +37,17 @@ func (c *x11Context) Release() {
} }
func (c *x11Context) Refresh() error { func (c *x11Context) Refresh() error {
c.Context.ReleaseSurface()
win, width, height := c.win.window()
eglSurf := egl.NativeWindowType(uintptr(win))
if err := c.Context.CreateSurface(eglSurf, width, height); err != nil {
return err
}
if err := c.Context.MakeCurrent(); err != nil {
return err
}
c.Context.EnableVSync(true)
c.Context.ReleaseCurrent()
return nil return nil
} }
+4
View File
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: Unlicense OR MIT // SPDX-License-Identifier: Unlicense OR MIT
//go:build darwin && ios && nometal //go:build darwin && ios && nometal
// +build darwin,ios,nometal
package app package app
@@ -115,6 +116,9 @@ func (c *context) Unlock() {
} }
func (c *context) Refresh() error { func (c *context) Refresh() error {
if C.gio_makeCurrent(c.ctx) == 0 {
return errors.New("[EAGLContext setCurrentContext] failed")
}
if !c.init { if !c.init {
c.init = true c.init = true
c.frameBuffer = c.c.CreateFramebuffer() c.frameBuffer = c.c.CreateFramebuffer()
+4
View File
@@ -1,12 +1,14 @@
// SPDX-License-Identifier: Unlicense OR MIT // SPDX-License-Identifier: Unlicense OR MIT
//go:build darwin && !ios && nometal //go:build darwin && !ios && nometal
// +build darwin,!ios,nometal
package app package app
import ( import (
"errors" "errors"
"runtime" "runtime"
"unsafe" "unsafe"
"gioui.org/gpu" "gioui.org/gpu"
@@ -111,6 +113,8 @@ func (c *glContext) Unlock() {
} }
func (c *glContext) Refresh() error { func (c *glContext) Refresh() error {
c.Lock()
defer c.Unlock()
C.gio_updateContext(c.ctx) C.gio_updateContext(c.ctx)
return nil return nil
} }
+1 -1
View File
@@ -7,7 +7,7 @@
#include <OpenGL/OpenGL.h> #include <OpenGL/OpenGL.h>
#include "_cgo_export.h" #include "_cgo_export.h"
CALayer *gio_layerFactory(BOOL presentWithTrans) { CALayer *gio_layerFactory(void) {
@autoreleasepool { @autoreleasepool {
return [CALayer layer]; return [CALayer layer];
} }
-145
View File
@@ -1,145 +0,0 @@
// SPDX-License-Identifier: Unlicense OR MIT
package app
import (
"unicode"
"unicode/utf16"
"unicode/utf8"
"gioui.org/io/input"
"gioui.org/io/key"
)
type editorState struct {
input.EditorState
compose key.Range
}
func (e *editorState) Replace(r key.Range, text string) {
if r.Start > r.End {
r.Start, r.End = r.End, r.Start
}
runes := []rune(text)
newEnd := r.Start + len(runes)
adjust := func(pos int) int {
switch {
case newEnd < pos && pos <= r.End:
return newEnd
case r.End < pos:
diff := newEnd - r.End
return pos + diff
}
return pos
}
e.Selection.Start = adjust(e.Selection.Start)
e.Selection.End = adjust(e.Selection.End)
if e.compose.Start != -1 {
e.compose.Start = adjust(e.compose.Start)
e.compose.End = adjust(e.compose.End)
}
s := e.Snippet
if r.End < s.Start || r.Start > s.End {
// Discard snippet if it doesn't overlap with replacement.
s = key.Snippet{
Range: key.Range{
Start: r.Start,
End: r.Start,
},
}
}
var newSnippet []rune
snippet := []rune(s.Text)
// Append first part of existing snippet.
if end := r.Start - s.Start; end > 0 {
newSnippet = append(newSnippet, snippet[:end]...)
}
// Append replacement.
newSnippet = append(newSnippet, runes...)
// Append last part of existing snippet.
if start := r.End; start < s.End {
newSnippet = append(newSnippet, snippet[start-s.Start:]...)
}
// Adjust snippet range to include replacement.
if r.Start < s.Start {
s.Start = r.Start
}
s.End = s.Start + len(newSnippet)
s.Text = string(newSnippet)
e.Snippet = s
}
// UTF16Index converts the given index in runes into an index in utf16 characters.
func (e *editorState) UTF16Index(runes int) int {
if runes == -1 {
return -1
}
if runes < e.Snippet.Start {
// Assume runes before sippet are one UTF-16 character each.
return runes
}
chars := e.Snippet.Start
runes -= e.Snippet.Start
for _, r := range e.Snippet.Text {
if runes == 0 {
break
}
runes--
chars++
if r1, _ := utf16.EncodeRune(r); r1 != unicode.ReplacementChar {
chars++
}
}
// Assume runes after snippets are one UTF-16 character each.
return chars + runes
}
// RunesIndex converts the given index in utf16 characters to an index in runes.
func (e *editorState) RunesIndex(chars int) int {
if chars == -1 {
return -1
}
if chars < e.Snippet.Start {
// Assume runes before offset are one UTF-16 character each.
return chars
}
runes := e.Snippet.Start
chars -= e.Snippet.Start
for _, r := range e.Snippet.Text {
if chars == 0 {
break
}
chars--
runes++
if r1, _ := utf16.EncodeRune(r); r1 != unicode.ReplacementChar {
chars--
}
}
// Assume runes after snippets are one UTF-16 character each.
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
}
+7 -7
View File
@@ -1,16 +1,18 @@
// SPDX-License-Identifier: Unlicense OR MIT // SPDX-License-Identifier: Unlicense OR MIT
//go:build go1.18
// +build go1.18
package app package app
import ( import (
"gioui.org/f32"
"testing" "testing"
"unicode/utf8" "unicode/utf8"
"gioui.org/font" "gioui.org/font"
"gioui.org/font/gofont" "gioui.org/font/gofont"
"gioui.org/io/input"
"gioui.org/io/key" "gioui.org/io/key"
"gioui.org/io/router"
"gioui.org/layout" "gioui.org/layout"
"gioui.org/op" "gioui.org/op"
"gioui.org/text" "gioui.org/text"
@@ -29,16 +31,15 @@ func FuzzIME(f *testing.F) {
f.Fuzz(func(t *testing.T, cmds []byte) { f.Fuzz(func(t *testing.T, cmds []byte) {
cache := text.NewShaper(text.WithCollection(gofont.Collection())) cache := text.NewShaper(text.WithCollection(gofont.Collection()))
e := new(widget.Editor) e := new(widget.Editor)
e.Focus()
var r input.Router var r router.Router
gtx := layout.Context{Ops: new(op.Ops), Source: r.Source()} gtx := layout.Context{Ops: new(op.Ops), Queue: &r}
gtx.Execute(key.FocusCmd{Tag: e})
// Layout once to register focus. // Layout once to register focus.
e.Layout(gtx, cache, font.Font{}, unit.Sp(10), op.CallOp{}, op.CallOp{}) e.Layout(gtx, cache, font.Font{}, unit.Sp(10), op.CallOp{}, op.CallOp{})
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
@@ -138,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,
+7
View File
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: Unlicense OR MIT
// Package points standard output, standard error and the standard
// library package log to the platform logger.
package log
var appID = "gio"
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Unlicense OR MIT // SPDX-License-Identifier: Unlicense OR MIT
package app package log
/* /*
#cgo LDFLAGS: -llog #cgo LDFLAGS: -llog
@@ -22,7 +22,7 @@ import (
// 1024 is the truncation limit from android/log.h, plus a \n. // 1024 is the truncation limit from android/log.h, plus a \n.
const logLineLimit = 1024 const logLineLimit = 1024
var logTag = C.CString(ID) var logTag = C.CString(appID)
func init() { func init() {
// Android's logcat already includes timestamps. // Android's logcat already includes timestamps.
@@ -1,8 +1,9 @@
// SPDX-License-Identifier: Unlicense OR MIT // SPDX-License-Identifier: Unlicense OR MIT
//go:build darwin && ios //go:build darwin && ios
// +build darwin,ios
package app package log
/* /*
#cgo CFLAGS: -Werror -fmodules -fobjc-arc -x objective-c #cgo CFLAGS: -Werror -fmodules -fobjc-arc -x objective-c
@@ -1,18 +1,17 @@
// SPDX-License-Identifier: Unlicense OR MIT // SPDX-License-Identifier: Unlicense OR MIT
package app package log
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
) )
+40 -178
View File
@@ -108,80 +108,6 @@ type MonitorInfo struct {
Flags uint32 Flags uint32
} }
type CopyDataStruct struct {
DwData uintptr
CbData uint32
LpData uintptr
}
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
@@ -318,52 +244,44 @@ 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_COPYDATA = 0x004A 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_NCHITTEST = 0x0084 WM_NCCALCSIZE = 0x0083
WM_NCCALCSIZE = 0x0083 WM_PAINT = 0x000F
WM_PAINT = 0x000F WM_QUIT = 0x0012
WM_POINTERCAPTURECHANGED = 0x024C WM_SETCURSOR = 0x0020
WM_POINTERDOWN = 0x0246 WM_SETFOCUS = 0x0007
WM_POINTERUP = 0x0247 WM_SHOWWINDOW = 0x0018
WM_POINTERUPDATE = 0x0245 WM_SIZE = 0x0005
WM_POINTERWHEEL = 0x024E WM_SYSKEYDOWN = 0x0104
WM_POINTERHWHEEL = 0x024F WM_SYSKEYUP = 0x0105
WM_QUIT = 0x0012 WM_RBUTTONDOWN = 0x0204
WM_RBUTTONDOWN = 0x0204 WM_RBUTTONUP = 0x0205
WM_RBUTTONUP = 0x0205 WM_TIMER = 0x0113
WM_SETCURSOR = 0x0020 WM_UNICHAR = 0x0109
WM_SETFOCUS = 0x0007 WM_USER = 0x0400
WM_SHOWWINDOW = 0x0018 WM_WINDOWPOSCHANGED = 0x0047
WM_SIZE = 0x0005
WM_STYLECHANGED = 0x007D
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
@@ -426,9 +344,7 @@ var (
_DefWindowProc = user32.NewProc("DefWindowProcW") _DefWindowProc = user32.NewProc("DefWindowProcW")
_DestroyWindow = user32.NewProc("DestroyWindow") _DestroyWindow = user32.NewProc("DestroyWindow")
_DispatchMessage = user32.NewProc("DispatchMessageW") _DispatchMessage = user32.NewProc("DispatchMessageW")
_FindWindow = user32.NewProc("FindWindowW")
_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")
@@ -438,7 +354,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")
@@ -456,11 +371,9 @@ 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")
_SendMessage = user32.NewProc("SendMessageW")
_SetCapture = user32.NewProc("SetCapture") _SetCapture = user32.NewProc("SetCapture")
_SetCursor = user32.NewProc("SetCursor") _SetCursor = user32.NewProc("SetCursor")
_SetClipboardData = user32.NewProc("SetClipboardData") _SetClipboardData = user32.NewProc("SetClipboardData")
@@ -513,10 +426,7 @@ func CloseClipboard() error {
} }
func CreateWindowEx(dwExStyle uint32, lpClassName uint16, lpWindowName string, dwStyle uint32, x, y, w, h int32, hWndParent, hMenu, hInstance syscall.Handle, lpParam uintptr) (syscall.Handle, error) { func CreateWindowEx(dwExStyle uint32, lpClassName uint16, lpWindowName string, dwStyle uint32, x, y, w, h int32, hWndParent, hMenu, hInstance syscall.Handle, lpParam uintptr) (syscall.Handle, error) {
wname, err := syscall.UTF16PtrFromString(lpWindowName) wname := syscall.StringToUTF16Ptr(lpWindowName)
if err != nil {
return 0, fmt.Errorf("CreateWindowEx failed: %v", err)
}
hwnd, _, err := _CreateWindowEx.Call( hwnd, _, err := _CreateWindowEx.Call(
uintptr(dwExStyle), uintptr(dwExStyle),
uintptr(lpClassName), uintptr(lpClassName),
@@ -534,31 +444,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
@@ -588,18 +473,6 @@ func EmptyClipboard() error {
return nil return nil
} }
func FindWindow(lpClassName string) (syscall.Handle, error) {
className, err := syscall.UTF16PtrFromString(lpClassName)
if err != nil {
return 0, fmt.Errorf("FindWindow failed: %v", err)
}
hwnd, _, err := _FindWindow.Call(uintptr(unsafe.Pointer(className)), 0)
if hwnd == 0 {
return 0, fmt.Errorf("FindWindow failed: %v", err)
}
return syscall.Handle(hwnd), nil
}
func GetWindowRect(hwnd syscall.Handle) Rect { func GetWindowRect(hwnd syscall.Handle) Rect {
var r Rect var r Rect
_GetWindowRect.Call(uintptr(hwnd), uintptr(unsafe.Pointer(&r))) _GetWindowRect.Call(uintptr(hwnd), uintptr(unsafe.Pointer(&r)))
@@ -791,10 +664,7 @@ func SetWindowPos(hwnd syscall.Handle, hwndInsertAfter uint32, x, y, dx, dy int3
} }
func SetWindowText(hwnd syscall.Handle, title string) { func SetWindowText(hwnd syscall.Handle, title string) {
wname, err := syscall.UTF16PtrFromString(title) wname := syscall.StringToUTF16Ptr(title)
if err != nil {
panic(err)
}
_SetWindowText.Call(uintptr(hwnd), uintptr(unsafe.Pointer(wname))) _SetWindowText.Call(uintptr(hwnd), uintptr(unsafe.Pointer(wname)))
} }
@@ -910,14 +780,6 @@ func ReleaseDC(hdc syscall.Handle) {
_ReleaseDC.Call(uintptr(hdc)) _ReleaseDC.Call(uintptr(hdc))
} }
func SendMessage(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) error {
r, _, err := _SendMessage.Call(uintptr(hwnd), uintptr(msg), wParam, lParam)
if r == 0 {
return fmt.Errorf("SendMessage failed: %v", err)
}
return nil
}
func SetForegroundWindow(hwnd syscall.Handle) { func SetForegroundWindow(hwnd syscall.Handle) {
_SetForegroundWindow.Call(uintptr(hwnd)) _SetForegroundWindow.Call(uintptr(hwnd))
} }
+5 -5
View File
@@ -238,17 +238,17 @@ func (x *Context) UpdateMask(depressed, latched, locked, depressedGroup, latched
C.xkb_layout_index_t(depressedGroup), C.xkb_layout_index_t(latchedGroup), C.xkb_layout_index_t(lockedGroup)) C.xkb_layout_index_t(depressedGroup), C.xkb_layout_index_t(latchedGroup), C.xkb_layout_index_t(lockedGroup))
} }
func convertKeysym(s C.xkb_keysym_t) (key.Name, bool) { func convertKeysym(s C.xkb_keysym_t) (string, bool) {
if 'a' <= s && s <= 'z' { if 'a' <= s && s <= 'z' {
return key.Name(rune(s - 'a' + 'A')), true return string(rune(s - 'a' + 'A')), true
} }
if C.XKB_KEY_KP_0 <= s && s <= C.XKB_KEY_KP_9 { if C.XKB_KEY_KP_0 <= s && s <= C.XKB_KEY_KP_9 {
return key.Name(rune(s - C.XKB_KEY_KP_0 + '0')), true return string(rune(s - C.XKB_KEY_KP_0 + '0')), true
} }
if ' ' < s && s <= '~' { if ' ' < s && s <= '~' {
return key.Name(rune(s)), true return string(rune(s)), true
} }
var n key.Name var n string
switch s { switch s {
case C.XKB_KEY_Escape: case C.XKB_KEY_Escape:
n = key.NameEscape n = key.NameEscape
+2 -3
View File
@@ -60,9 +60,8 @@ static void presentDrawable(CFTypeRef queueRef, CFTypeRef drawableRef) {
id<MTLDrawable> drawable = (__bridge id<MTLDrawable>)drawableRef; id<MTLDrawable> drawable = (__bridge id<MTLDrawable>)drawableRef;
id<MTLCommandQueue> queue = (__bridge id<MTLCommandQueue>)queueRef; id<MTLCommandQueue> queue = (__bridge id<MTLCommandQueue>)queueRef;
id<MTLCommandBuffer> cmdBuffer = [queue commandBuffer]; id<MTLCommandBuffer> cmdBuffer = [queue commandBuffer];
[cmdBuffer presentDrawable:drawable];
[cmdBuffer commit]; [cmdBuffer commit];
[cmdBuffer waitUntilScheduled];
[drawable present];
} }
} }
@@ -96,7 +95,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 -4
View File
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: Unlicense OR MIT // SPDX-License-Identifier: Unlicense OR MIT
//go:build !nometal //go:build !nometal
// +build !nometal
package app package app
@@ -20,10 +21,7 @@ Class gio_layerClass(void) {
static CFTypeRef getMetalLayer(CFTypeRef viewRef) { static CFTypeRef getMetalLayer(CFTypeRef viewRef) {
@autoreleasepool { @autoreleasepool {
UIView *view = (__bridge UIView *)viewRef; UIView *view = (__bridge UIView *)viewRef;
CAMetalLayer *l = (CAMetalLayer *)view.layer; return CFBridgingRetain(view.layer);
l.needsDisplayOnBoundsChange = YES;
l.presentsWithTransaction = YES;
return CFBridgingRetain(l);
} }
} }
+2 -6
View File
@@ -12,13 +12,9 @@ package app
#import <QuartzCore/CAMetalLayer.h> #import <QuartzCore/CAMetalLayer.h>
#include <CoreFoundation/CoreFoundation.h> #include <CoreFoundation/CoreFoundation.h>
CALayer *gio_layerFactory(BOOL presentWithTrans) { CALayer *gio_layerFactory(void) {
@autoreleasepool { @autoreleasepool {
CAMetalLayer *l = [CAMetalLayer layer]; return [CAMetalLayer layer];
l.autoresizingMask = kCALayerHeightSizable|kCALayerWidthSizable;
l.needsDisplayOnBoundsChange = YES;
l.presentsWithTransaction = presentWithTrans;
return l;
} }
} }
+22 -168
View File
@@ -7,9 +7,7 @@ import (
"image" "image"
"image/color" "image/color"
"gioui.org/io/event"
"gioui.org/io/key" "gioui.org/io/key"
"gioui.org/op"
"gioui.org/gpu" "gioui.org/gpu"
"gioui.org/io/pointer" "gioui.org/io/pointer"
@@ -45,10 +43,6 @@ type Config struct {
CustomRenderer bool CustomRenderer bool
// Decorated reports whether window decorations are provided automatically. // Decorated reports whether window decorations are provided automatically.
Decorated bool Decorated bool
// TopMost windows render above all other non-top-most windows.
TopMost bool
// Focused reports whether the window is focused.
Focused bool
// decoHeight is the height of the fallback decoration for platforms such // decoHeight is the height of the fallback decoration for platforms such
// as Wayland that may need fallback client-side decorations. // as Wayland that may need fallback client-side decorations.
decoHeight unit.Dp decoHeight unit.Dp
@@ -137,36 +131,12 @@ func (o Orientation) String() string {
return "" return ""
} }
// eventLoop implements the functionality required for drivers where
// window event loops must run on a separate thread.
type eventLoop struct {
win *callbacks
// wakeup is the callback to wake up the event loop.
wakeup func()
// driverFuncs is a channel of functions to run the next
// time the window loop waits for events.
driverFuncs chan func()
// invalidates is notified when an invalidate is requested by the client.
invalidates chan struct{}
// immediateInvalidates is an optimistic invalidates that doesn't require a wakeup.
immediateInvalidates chan struct{}
// events is where the platform backend delivers events bound for the
// user program.
events chan event.Event
frames chan *op.Ops
frameAck chan struct{}
// delivering avoids re-entrant event delivery.
delivering bool
}
type frameEvent struct { type frameEvent struct {
FrameEvent system.FrameEvent
Sync bool Sync bool
} }
// The caller must hold the context lock while using API, Refresh,
// RenderTarget, or Present.
type context interface { type context interface {
API() gpu.API API() gpu.API
RenderTarget() (gpu.RenderTarget, error) RenderTarget() (gpu.RenderTarget, error)
@@ -177,13 +147,9 @@ type context interface {
Unlock() Unlock()
} }
// driver is the interface for the platform implementation // Driver is the interface for the platform implementation
// of a window. // of a window.
type driver interface { type driver interface {
// Event blocks until an event is available and returns it.
Event() event.Event
// Invalidate requests a FrameEvent.
Invalidate()
// SetAnimating sets the animation flag. When the window is animating, // SetAnimating sets the animation flag. When the window is animating,
// FrameEvents are delivered as fast as the display can handle them. // FrameEvents are delivered as fast as the display can handle them.
SetAnimating(anim bool) SetAnimating(anim bool)
@@ -194,27 +160,23 @@ type driver interface {
// ReadClipboard requests the clipboard content. // ReadClipboard requests the clipboard content.
ReadClipboard() ReadClipboard()
// WriteClipboard requests a clipboard write. // WriteClipboard requests a clipboard write.
WriteClipboard(mime string, s []byte) WriteClipboard(s string)
// Configure the window. // Configure the window.
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.
EditorStateChanged(old, new editorState) EditorStateChanged(old, new editorState)
// Run a function on the window thread.
Run(f func())
// Frame receives a frame.
Frame(frame *op.Ops)
// ProcessEvent processes an event.
ProcessEvent(e event.Event)
} }
type windowRendezvous struct { type windowRendezvous struct {
in chan windowAndConfig in chan windowAndConfig
out chan windowAndConfig out chan windowAndConfig
windows chan struct{} errs chan error
} }
type windowAndConfig struct { type windowAndConfig struct {
@@ -224,137 +186,32 @@ type windowAndConfig struct {
func newWindowRendezvous() *windowRendezvous { func newWindowRendezvous() *windowRendezvous {
wr := &windowRendezvous{ wr := &windowRendezvous{
in: make(chan windowAndConfig), in: make(chan windowAndConfig),
out: make(chan windowAndConfig), out: make(chan windowAndConfig),
windows: make(chan struct{}), errs: make(chan error),
} }
go func() { go func() {
in := wr.in var main windowAndConfig
var window windowAndConfig
var out chan windowAndConfig var out chan windowAndConfig
for { for {
select { select {
case w := <-in: case w := <-wr.in:
window = w var err error
if main.window != nil {
err = errors.New("multiple windows are not supported")
}
wr.errs <- err
main = w
out = wr.out out = wr.out
case out <- window: case out <- main:
} }
} }
}() }()
return wr return wr
} }
func newEventLoop(w *callbacks, wakeup func()) *eventLoop { func (wakeupEvent) ImplementsEvent() {}
return &eventLoop{ func (ConfigEvent) ImplementsEvent() {}
win: w,
wakeup: wakeup,
events: make(chan event.Event),
invalidates: make(chan struct{}, 1),
immediateInvalidates: make(chan struct{}),
frames: make(chan *op.Ops),
frameAck: make(chan struct{}),
driverFuncs: make(chan func(), 1),
}
}
// Frame receives a frame and waits for its processing. It is called by
// the client goroutine.
func (e *eventLoop) Frame(frame *op.Ops) {
e.frames <- frame
<-e.frameAck
}
// Event returns the next available event. It is called by the client
// goroutine.
func (e *eventLoop) Event() event.Event {
for {
evt := <-e.events
// Receiving a flushEvent indicates to the platform backend that
// all previous events have been processed by the user program.
if _, ok := evt.(flushEvent); ok {
continue
}
return evt
}
}
// Invalidate requests invalidation of the window. It is called by the client
// goroutine.
func (e *eventLoop) Invalidate() {
select {
case e.immediateInvalidates <- struct{}{}:
// The event loop was waiting, no need for a wakeup.
case e.invalidates <- struct{}{}:
// The event loop is sleeping, wake it up.
e.wakeup()
default:
// A redraw is pending.
}
}
// Run f in the window loop thread. It is called by the client goroutine.
func (e *eventLoop) Run(f func()) {
e.driverFuncs <- f
e.wakeup()
}
// FlushEvents delivers pending events to the client.
func (e *eventLoop) FlushEvents() {
if e.delivering {
return
}
e.delivering = true
defer func() { e.delivering = false }()
for {
evt, ok := e.win.nextEvent()
if !ok {
break
}
e.deliverEvent(evt)
}
}
func (e *eventLoop) deliverEvent(evt event.Event) {
var frames <-chan *op.Ops
for {
select {
case f := <-e.driverFuncs:
f()
case frame := <-frames:
// The client called FrameEvent.Frame.
frames = nil
e.win.ProcessFrame(frame, e.frameAck)
case e.events <- evt:
switch evt.(type) {
case flushEvent, DestroyEvent:
// DestroyEvents are not flushed.
return
case FrameEvent:
frames = e.frames
}
evt = theFlushEvent
case <-e.invalidates:
e.win.Invalidate()
case <-e.immediateInvalidates:
e.win.Invalidate()
}
}
}
func (e *eventLoop) Wakeup() {
for {
select {
case f := <-e.driverFuncs:
f()
case <-e.invalidates:
e.win.Invalidate()
case <-e.immediateInvalidates:
e.win.Invalidate()
default:
return
}
}
}
func walkActions(actions system.Action, do func(system.Action)) { func walkActions(actions system.Action, do func(system.Action)) {
for a := system.Action(1); actions != 0; a <<= 1 { for a := system.Action(1); actions != 0; a <<= 1 {
@@ -364,6 +221,3 @@ func walkActions(actions system.Action, do func(system.Action)) {
} }
} }
} }
func (wakeupEvent) ImplementsEvent() {}
func (ConfigEvent) ImplementsEvent() {}
+105 -156
View File
@@ -123,29 +123,24 @@ import (
"fmt" "fmt"
"image" "image"
"image/color" "image/color"
"io"
"math" "math"
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
"runtime/cgo" "runtime/cgo"
"runtime/debug" "runtime/debug"
"strings"
"sync" "sync"
"time" "time"
"unicode/utf16" "unicode/utf16"
"unsafe" "unsafe"
"gioui.org/io/transfer"
"gioui.org/internal/f32color" "gioui.org/internal/f32color"
"gioui.org/op"
"gioui.org/f32" "gioui.org/f32"
"gioui.org/io/event" "gioui.org/io/clipboard"
"gioui.org/io/input"
"gioui.org/io/key" "gioui.org/io/key"
"gioui.org/io/pointer" "gioui.org/io/pointer"
"gioui.org/io/router"
"gioui.org/io/semantic" "gioui.org/io/semantic"
"gioui.org/io/system" "gioui.org/io/system"
"gioui.org/unit" "gioui.org/unit"
@@ -153,7 +148,6 @@ import (
type window struct { type window struct {
callbacks *callbacks callbacks *callbacks
loop *eventLoop
view C.jobject view C.jobject
handle cgo.Handle handle cgo.Handle
@@ -162,19 +156,18 @@ type window struct {
fontScale float32 fontScale float32
insets pixelInsets insets pixelInsets
visible bool stage system.Stage
started bool started bool
animating bool animating bool
win *C.ANativeWindow win *C.ANativeWindow
config Config config Config
inputHint key.InputHint
semantic struct { semantic struct {
hoverID input.SemanticID hoverID router.SemanticID
rootID input.SemanticID rootID router.SemanticID
focusID input.SemanticID focusID router.SemanticID
diffs []input.SemanticID diffs []router.SemanticID
} }
} }
@@ -206,9 +199,9 @@ type pixelInsets struct {
top, bottom, left, right int top, bottom, left, right int
} }
// AndroidViewEvent is sent whenever the Window's underlying Android view // ViewEvent is sent whenever the Window's underlying Android view
// changes. // changes.
type AndroidViewEvent struct { type ViewEvent struct {
// View is a JNI global reference to the android.view.View // View is a JNI global reference to the android.view.View
// instance backing the Window. The reference is valid until // instance backing the Window. The reference is valid until
// the next ViewEvent is received. // the next ViewEvent is received.
@@ -218,6 +211,8 @@ type AndroidViewEvent struct {
type jvalue uint64 // The largest JNI type fits in 64 bits. type jvalue uint64 // The largest JNI type fits in 64 bits.
var dataDirChan = make(chan string, 1)
var android struct { var android struct {
// mu protects all fields of this structure. However, once a // mu protects all fields of this structure. However, once a
// non-nil jvm is returned from javaVM, all the other fields may // non-nil jvm is returned from javaVM, all the other fields may
@@ -293,7 +288,8 @@ var mainWindow = newWindowRendezvous()
var mainFuncs = make(chan func(env *C.JNIEnv), 1) var mainFuncs = make(chan func(env *C.JNIEnv), 1)
var ( var (
dataPath string dataDirOnce sync.Once
dataPath string
) )
var ( var (
@@ -340,9 +336,9 @@ func (w *window) NewContext() (context, error) {
} }
func dataDir() (string, error) { func dataDir() (string, error) {
if dataPath == "" { dataDirOnce.Do(func() {
panic("DataDir isn't valid before main") dataPath = <-dataDirChan
} })
return dataPath, nil return dataPath, nil
} }
@@ -395,7 +391,7 @@ func Java_org_gioui_Gio_runGoMain(env *C.JNIEnv, class C.jclass, jdataDir C.jbyt
os.Setenv("HOME", dataDir) os.Setenv("HOME", dataDir)
} }
dataPath = dataDir dataDirChan <- dataDir
C.jni_ReleaseByteArrayElements(env, jdataDir, dirBytes) C.jni_ReleaseByteArrayElements(env, jdataDir, dirBytes)
runMain() runMain()
@@ -489,30 +485,24 @@ func Java_org_gioui_GioView_onCreateView(env *C.JNIEnv, class C.jclass, view C.j
}) })
view = C.jni_NewGlobalRef(env, view) view = C.jni_NewGlobalRef(env, view)
wopts := <-mainWindow.out wopts := <-mainWindow.out
var cnf Config
w, ok := windows[wopts.window] w, ok := windows[wopts.window]
if !ok { if !ok {
w = &window{ w = &window{
callbacks: wopts.window, callbacks: wopts.window,
} }
w.loop = newEventLoop(w.callbacks, w.wakeup)
w.callbacks.SetDriver(w)
cnf.apply(unit.Metric{}, wopts.options)
windows[wopts.window] = w windows[wopts.window] = w
} else {
cnf = w.config
} }
mainWindow.windows <- struct{}{}
if w.view != 0 { if w.view != 0 {
w.detach(env) w.detach(env)
} }
w.view = view w.view = view
w.visible = false
w.handle = cgo.NewHandle(w) w.handle = cgo.NewHandle(w)
w.callbacks.SetDriver(w)
w.loadConfig(env, class) w.loadConfig(env, class)
w.setConfig(env, cnf) w.Configure(wopts.options)
w.SetInputHint(w.inputHint) w.SetInputHint(key.HintAny)
w.processEvent(AndroidViewEvent{View: uintptr(view)}) w.setStage(system.StagePaused)
w.callbacks.Event(ViewEvent{View: uintptr(view)})
return C.jlong(w.handle) return C.jlong(w.handle)
} }
@@ -526,7 +516,7 @@ func Java_org_gioui_GioView_onDestroyView(env *C.JNIEnv, class C.jclass, handle
func Java_org_gioui_GioView_onStopView(env *C.JNIEnv, class C.jclass, handle C.jlong) { func Java_org_gioui_GioView_onStopView(env *C.JNIEnv, class C.jclass, handle C.jlong) {
w := cgo.Handle(handle).Value().(*window) w := cgo.Handle(handle).Value().(*window)
w.started = false w.started = false
w.visible = false w.setStage(system.StagePaused)
} }
//export Java_org_gioui_GioView_onStartView //export Java_org_gioui_GioView_onStartView
@@ -542,7 +532,7 @@ func Java_org_gioui_GioView_onStartView(env *C.JNIEnv, class C.jclass, handle C.
func Java_org_gioui_GioView_onSurfaceDestroyed(env *C.JNIEnv, class C.jclass, handle C.jlong) { func Java_org_gioui_GioView_onSurfaceDestroyed(env *C.JNIEnv, class C.jclass, handle C.jlong) {
w := cgo.Handle(handle).Value().(*window) w := cgo.Handle(handle).Value().(*window)
w.win = nil w.win = nil
w.visible = false w.setStage(system.StagePaused)
} }
//export Java_org_gioui_GioView_onSurfaceChanged //export Java_org_gioui_GioView_onSurfaceChanged
@@ -564,7 +554,9 @@ func Java_org_gioui_GioView_onLowMemory(env *C.JNIEnv, class C.jclass) {
func Java_org_gioui_GioView_onConfigurationChanged(env *C.JNIEnv, class C.jclass, view C.jlong) { func Java_org_gioui_GioView_onConfigurationChanged(env *C.JNIEnv, class C.jclass, view C.jlong) {
w := cgo.Handle(view).Value().(*window) w := cgo.Handle(view).Value().(*window)
w.loadConfig(env, class) w.loadConfig(env, class)
w.draw(env, true) if w.stage >= system.StageInactive {
w.draw(env, true)
}
} }
//export Java_org_gioui_GioView_onFrameCallback //export Java_org_gioui_GioView_onFrameCallback
@@ -573,13 +565,19 @@ 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.stage < system.StageInactive {
return
}
if w.animating {
w.draw(env, false)
callVoidMethod(env, w.view, gioView.postFrameCallback)
}
} }
//export Java_org_gioui_GioView_onBack //export Java_org_gioui_GioView_onBack
func Java_org_gioui_GioView_onBack(env *C.JNIEnv, class C.jclass, view C.jlong) C.jboolean { func Java_org_gioui_GioView_onBack(env *C.JNIEnv, class C.jclass, view C.jlong) C.jboolean {
w := cgo.Handle(view).Value().(*window) w := cgo.Handle(view).Value().(*window)
if w.processEvent(key.Event{Name: key.NameBack}) { if w.callbacks.Event(key.Event{Name: key.NameBack}) {
return C.JNI_TRUE return C.JNI_TRUE
} }
return C.JNI_FALSE return C.JNI_FALSE
@@ -588,8 +586,7 @@ func Java_org_gioui_GioView_onBack(env *C.JNIEnv, class C.jclass, view C.jlong)
//export Java_org_gioui_GioView_onFocusChange //export Java_org_gioui_GioView_onFocusChange
func Java_org_gioui_GioView_onFocusChange(env *C.JNIEnv, class C.jclass, view C.jlong, focus C.jboolean) { func Java_org_gioui_GioView_onFocusChange(env *C.JNIEnv, class C.jclass, view C.jlong, focus C.jboolean) {
w := cgo.Handle(view).Value().(*window) w := cgo.Handle(view).Value().(*window)
w.config.Focused = focus == C.JNI_TRUE w.callbacks.Event(key.FocusEvent{Focus: focus == C.JNI_TRUE})
w.processEvent(ConfigEvent{Config: w.config})
} }
//export Java_org_gioui_GioView_onWindowInsets //export Java_org_gioui_GioView_onWindowInsets
@@ -601,7 +598,9 @@ func Java_org_gioui_GioView_onWindowInsets(env *C.JNIEnv, class C.jclass, view C
left: int(left), left: int(left),
right: int(right), right: int(right),
} }
w.draw(env, true) if w.stage >= system.StageInactive {
w.draw(env, true)
}
} }
//export Java_org_gioui_GioView_initializeAccessibilityNodeInfo //export Java_org_gioui_GioView_initializeAccessibilityNodeInfo
@@ -662,44 +661,7 @@ func Java_org_gioui_GioView_onClearA11yFocus(env *C.JNIEnv, class C.jclass, view
} }
} }
//export Java_org_gioui_GioView_onOpenURI func (w *window) initAccessibilityNodeInfo(env *C.JNIEnv, sem router.SemanticNode, off image.Point, info C.jobject) error {
func Java_org_gioui_GioView_onOpenURI(env *C.JNIEnv, class C.jclass, view C.jlong, uri C.jstring) {
evt, err := newURLEvent(goString(env, uri))
if err != nil {
return
}
processGlobalEvent(evt)
}
func (w *window) ProcessEvent(e event.Event) {
w.processEvent(e)
}
func (w *window) processEvent(e event.Event) bool {
if !w.callbacks.ProcessEvent(e) {
return false
}
w.loop.FlushEvents()
return true
}
func (w *window) Event() event.Event {
return w.loop.Event()
}
func (w *window) Invalidate() {
w.loop.Invalidate()
}
func (w *window) Run(f func()) {
w.loop.Run(f)
}
func (w *window) Frame(frame *op.Ops) {
w.loop.Frame(frame)
}
func (w *window) initAccessibilityNodeInfo(env *C.JNIEnv, sem input.SemanticNode, off image.Point, info C.jobject) error {
for _, ch := range sem.Children { for _, ch := range sem.Children {
err := callVoidMethod(env, info, android.accessibilityNodeInfo.addChild, jvalue(w.view), jvalue(w.virtualIDFor(ch.ID))) err := callVoidMethod(env, info, android.accessibilityNodeInfo.addChild, jvalue(w.view), jvalue(w.virtualIDFor(ch.ID)))
if err != nil { if err != nil {
@@ -742,7 +704,7 @@ func (w *window) initAccessibilityNodeInfo(env *C.JNIEnv, sem input.SemanticNode
panic(err) panic(err)
} }
} }
if d.Gestures&input.ClickGesture != 0 { if d.Gestures&router.ClickGesture != 0 {
addAction(ACTION_CLICK) addAction(ACTION_CLICK)
} }
clsName := android.strings.androidViewView clsName := android.strings.androidViewView
@@ -787,23 +749,25 @@ func (w *window) initAccessibilityNodeInfo(env *C.JNIEnv, sem input.SemanticNode
return nil return nil
} }
func (w *window) virtualIDFor(id input.SemanticID) C.jint { func (w *window) virtualIDFor(id router.SemanticID) C.jint {
// TODO: Android virtual IDs are 32-bit Java integers, but childID is a int64.
if id == w.semantic.rootID { if id == w.semantic.rootID {
return HOST_VIEW_ID return HOST_VIEW_ID
} }
return C.jint(id) return C.jint(id)
} }
func (w *window) semIDFor(virtID C.jint) input.SemanticID { func (w *window) semIDFor(virtID C.jint) router.SemanticID {
if virtID == HOST_VIEW_ID { if virtID == HOST_VIEW_ID {
return w.semantic.rootID return w.semantic.rootID
} }
return input.SemanticID(virtID) return router.SemanticID(virtID)
} }
func (w *window) detach(env *C.JNIEnv) { func (w *window) detach(env *C.JNIEnv) {
callVoidMethod(env, w.view, gioView.unregister) callVoidMethod(env, w.view, gioView.unregister)
w.processEvent(AndroidViewEvent{}) w.callbacks.Event(ViewEvent{})
w.callbacks.SetDriver(nil)
w.handle.Delete() w.handle.Delete()
C.jni_DeleteGlobalRef(env, w.view) C.jni_DeleteGlobalRef(env, w.view)
w.view = 0 w.view = 0
@@ -814,10 +778,18 @@ func (w *window) setVisible(env *C.JNIEnv) {
if width == 0 || height == 0 { if width == 0 || height == 0 {
return return
} }
w.visible = true w.setStage(system.StageRunning)
w.draw(env, true) w.draw(env, true)
} }
func (w *window) setStage(stage system.Stage) {
if stage == w.stage {
return
}
w.stage = stage
w.callbacks.Event(system.StageEvent{stage})
}
func (w *window) setVisual(visID int) error { func (w *window) setVisual(visID int) error {
if C.ANativeWindow_setBuffersGeometry(w.win, 0, 0, C.int32_t(visID)) != 0 { if C.ANativeWindow_setBuffersGeometry(w.win, 0, 0, C.int32_t(visID)) != 0 {
return errors.New("ANativeWindow_setBuffersGeometry failed") return errors.New("ANativeWindow_setBuffersGeometry failed")
@@ -854,13 +826,10 @@ func (w *window) SetAnimating(anim bool) {
} }
func (w *window) draw(env *C.JNIEnv, sync bool) { func (w *window) draw(env *C.JNIEnv, sync bool) {
if !w.visible {
return
}
size := image.Pt(int(C.ANativeWindow_getWidth(w.win)), int(C.ANativeWindow_getHeight(w.win))) size := image.Pt(int(C.ANativeWindow_getWidth(w.win)), int(C.ANativeWindow_getHeight(w.win)))
if size != w.config.Size { if size != w.config.Size {
w.config.Size = size w.config.Size = size
w.processEvent(ConfigEvent{Config: w.config}) w.callbacks.Event(ConfigEvent{Config: w.config})
} }
if size.X == 0 || size.Y == 0 { if size.X == 0 || size.Y == 0 {
return return
@@ -868,14 +837,14 @@ func (w *window) draw(env *C.JNIEnv, sync bool) {
const inchPrDp = 1.0 / 160 const inchPrDp = 1.0 / 160
ppdp := float32(w.dpi) * inchPrDp ppdp := float32(w.dpi) * inchPrDp
dppp := unit.Dp(1.0 / ppdp) dppp := unit.Dp(1.0 / ppdp)
insets := Insets{ insets := system.Insets{
Top: unit.Dp(w.insets.top) * dppp, Top: unit.Dp(w.insets.top) * dppp,
Bottom: unit.Dp(w.insets.bottom) * dppp, Bottom: unit.Dp(w.insets.bottom) * dppp,
Left: unit.Dp(w.insets.left) * dppp, Left: unit.Dp(w.insets.left) * dppp,
Right: unit.Dp(w.insets.right) * dppp, Right: unit.Dp(w.insets.right) * dppp,
} }
w.processEvent(frameEvent{ w.callbacks.Event(frameEvent{
FrameEvent: FrameEvent{ FrameEvent: system.FrameEvent{
Now: time.Now(), Now: time.Now(),
Size: w.config.Size, Size: w.config.Size,
Insets: insets, Insets: insets,
@@ -886,9 +855,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)
@@ -932,8 +898,8 @@ func runInJVM(jvm *C.JavaVM, f func(env *C.JNIEnv)) {
f(env) f(env)
} }
func convertKeyCode(code C.jint) (key.Name, bool) { func convertKeyCode(code C.jint) (string, bool) {
var n key.Name var n string
switch code { switch code {
case C.AKEYCODE_FORWARD_DEL: case C.AKEYCODE_FORWARD_DEL:
n = key.NameDeleteForward n = key.NameDeleteForward
@@ -977,7 +943,7 @@ func Java_org_gioui_GioView_onKeyEvent(env *C.JNIEnv, class C.jclass, handle C.j
if pressed == C.JNI_TRUE { if pressed == C.JNI_TRUE {
state = key.Press state = key.Press
} }
w.processEvent(key.Event{Name: n, State: state}) w.callbacks.Event(key.Event{Name: n, State: state})
} }
if pressed == C.JNI_TRUE && r != 0 && r != '\n' { // Checking for "\n" to prevent duplication with key.NameEnter (gio#224). if pressed == C.JNI_TRUE && r != 0 && r != '\n' { // Checking for "\n" to prevent duplication with key.NameEnter (gio#224).
w.callbacks.EditorInsert(string(rune(r))) w.callbacks.EditorInsert(string(rune(r)))
@@ -1027,7 +993,7 @@ func Java_org_gioui_GioView_onTouchEvent(env *C.JNIEnv, class C.jclass, handle C
default: default:
return return
} }
w.processEvent(pointer.Event{ w.callbacks.Event(pointer.Event{
Kind: kind, Kind: kind,
Source: src, Source: src,
Buttons: btns, Buttons: btns,
@@ -1179,8 +1145,6 @@ func (w *window) ShowTextInput(show bool) {
} }
func (w *window) SetInputHint(mode key.InputHint) { func (w *window) SetInputHint(mode key.InputHint) {
w.inputHint = mode
// Constants defined at https://developer.android.com/reference/android/text/InputType. // Constants defined at https://developer.android.com/reference/android/text/InputType.
const ( const (
TYPE_NULL = 0 TYPE_NULL = 0
@@ -1325,17 +1289,16 @@ func findClass(env *C.JNIEnv, name string) C.jclass {
} }
func osMain() { func osMain() {
select {}
} }
func newWindow(window *callbacks, options []Option) { func newWindow(window *callbacks, options []Option) error {
mainWindow.in <- windowAndConfig{window, options} mainWindow.in <- windowAndConfig{window, options}
<-mainWindow.windows return <-mainWindow.errs
} }
func (w *window) WriteClipboard(mime string, s []byte) { func (w *window) WriteClipboard(s string) {
runInJVM(javaVM(), func(env *C.JNIEnv) { runInJVM(javaVM(), func(env *C.JNIEnv) {
jstr := javaString(env, string(s)) jstr := javaString(env, s)
callStaticVoidMethod(env, android.gioCls, android.mwriteClipboard, callStaticVoidMethod(env, android.gioCls, android.mwriteClipboard,
jvalue(android.appCtx), jvalue(jstr)) jvalue(android.appCtx), jvalue(jstr))
}) })
@@ -1349,54 +1312,45 @@ func (w *window) ReadClipboard() {
return return
} }
content := goString(env, C.jstring(c)) content := goString(env, C.jstring(c))
w.processEvent(transfer.DataEvent{ w.callbacks.Event(clipboard.Event{Text: content})
Type: "application/text",
Open: func() io.ReadCloser {
return io.NopCloser(strings.NewReader(content))
},
})
}) })
} }
func (w *window) Configure(options []Option) { func (w *window) Configure(options []Option) {
cnf := w.config
cnf.apply(unit.Metric{}, options)
runInJVM(javaVM(), func(env *C.JNIEnv) { runInJVM(javaVM(), func(env *C.JNIEnv) {
w.setConfig(env, cnf) prev := w.config
}) cnf := w.config
} cnf.apply(unit.Metric{}, options)
// Decorations are never disabled.
cnf.Decorated = true
func (w *window) setConfig(env *C.JNIEnv, cnf Config) { if prev.Orientation != cnf.Orientation {
prev := w.config w.config.Orientation = cnf.Orientation
// Decorations are never disabled. setOrientation(env, w.view, cnf.Orientation)
cnf.Decorated = true
if prev.Orientation != cnf.Orientation {
w.config.Orientation = cnf.Orientation
setOrientation(env, w.view, cnf.Orientation)
}
if prev.NavigationColor != cnf.NavigationColor {
w.config.NavigationColor = cnf.NavigationColor
setNavigationColor(env, w.view, cnf.NavigationColor)
}
if prev.StatusColor != cnf.StatusColor {
w.config.StatusColor = cnf.StatusColor
setStatusColor(env, w.view, cnf.StatusColor)
}
if prev.Mode != cnf.Mode {
switch cnf.Mode {
case Fullscreen:
callVoidMethod(env, w.view, gioView.setFullscreen, C.JNI_TRUE)
w.config.Mode = Fullscreen
case Windowed:
callVoidMethod(env, w.view, gioView.setFullscreen, C.JNI_FALSE)
w.config.Mode = Windowed
} }
} if prev.NavigationColor != cnf.NavigationColor {
if cnf.Decorated != prev.Decorated { w.config.NavigationColor = cnf.NavigationColor
w.config.Decorated = cnf.Decorated setNavigationColor(env, w.view, cnf.NavigationColor)
} }
w.processEvent(ConfigEvent{Config: w.config}) if prev.StatusColor != cnf.StatusColor {
w.config.StatusColor = cnf.StatusColor
setStatusColor(env, w.view, cnf.StatusColor)
}
if prev.Mode != cnf.Mode {
switch cnf.Mode {
case Fullscreen:
callVoidMethod(env, w.view, gioView.setFullscreen, C.JNI_TRUE)
w.config.Mode = Fullscreen
case Windowed:
callVoidMethod(env, w.view, gioView.setFullscreen, C.JNI_FALSE)
w.config.Mode = Windowed
}
}
if cnf.Decorated != prev.Decorated {
w.config.Decorated = cnf.Decorated
}
w.callbacks.Event(ConfigEvent{Config: w.config})
})
} }
func (w *window) Perform(system.Action) {} func (w *window) Perform(system.Action) {}
@@ -1407,10 +1361,9 @@ func (w *window) SetCursor(cursor pointer.Cursor) {
}) })
} }
func (w *window) wakeup() { func (w *window) Wakeup() {
runOnMain(func(env *C.JNIEnv) { runOnMain(func(env *C.JNIEnv) {
w.loop.Wakeup() w.callbacks.Event(wakeupEvent{})
w.loop.FlushEvents()
}) })
} }
@@ -1501,8 +1454,4 @@ func Java_org_gioui_Gio_scheduleMainFuncs(env *C.JNIEnv, cls C.jclass) {
} }
} }
func (AndroidViewEvent) implementsViewEvent() {} func (_ ViewEvent) ImplementsEvent() {}
func (AndroidViewEvent) ImplementsEvent() {}
func (a AndroidViewEvent) Valid() bool {
return a != (AndroidViewEvent{})
}
+19 -17
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,25 +73,30 @@ type displayLink struct {
// displayLinks maps CFTypeRefs to *displayLinks. // displayLinks maps CFTypeRefs to *displayLinks.
var displayLinks sync.Map var displayLinks sync.Map
func isMainThread() bool { var mainFuncs = make(chan func(), 1)
return bool(C.isMainThread())
}
// runOnMain runs the function on the main thread. // runOnMain runs the function on the main thread.
func runOnMain(f func()) { func runOnMain(f func()) {
if isMainThread() { if C.isMainThread() {
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.
@@ -257,9 +260,8 @@ func windowSetCursor(from, to pointer.Cursor) pointer.Cursor {
return to return to
} }
func (w *window) wakeup() { func (w *window) Wakeup() {
runOnMain(func() { runOnMain(func() {
w.loop.Wakeup() w.w.Event(wakeupEvent{})
w.loop.FlushEvents()
}) })
} }
+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();
}); });
} }
+66 -163
View File
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: Unlicense OR MIT // SPDX-License-Identifier: Unlicense OR MIT
//go:build darwin && ios //go:build darwin && ios
// +build darwin,ios
package app package app
@@ -11,9 +12,6 @@ package app
#include <UIKit/UIKit.h> #include <UIKit/UIKit.h>
#include <stdint.h> #include <stdint.h>
__attribute__ ((visibility ("hidden"))) int gio_applicationMain(int argc, char *argv[]);
__attribute__ ((visibility ("hidden"))) void gio_viewSetHandle(CFTypeRef viewRef, uintptr_t handle);
struct drawParams { struct drawParams {
CGFloat dpi, sdpi; CGFloat dpi, sdpi;
CGFloat width, height; CGFloat width, height;
@@ -21,7 +19,6 @@ struct drawParams {
}; };
static void writeClipboard(unichar *chars, NSUInteger length) { static void writeClipboard(unichar *chars, NSUInteger length) {
#if !TARGET_OS_TV
@autoreleasepool { @autoreleasepool {
NSString *s = [NSString string]; NSString *s = [NSString string];
if (length > 0) { if (length > 0) {
@@ -30,18 +27,13 @@ static void writeClipboard(unichar *chars, NSUInteger length) {
UIPasteboard *p = UIPasteboard.generalPasteboard; UIPasteboard *p = UIPasteboard.generalPasteboard;
p.string = s; p.string = s;
} }
#endif
} }
static CFTypeRef readClipboard(void) { static CFTypeRef readClipboard(void) {
#if !TARGET_OS_TV
@autoreleasepool { @autoreleasepool {
UIPasteboard *p = UIPasteboard.generalPasteboard; UIPasteboard *p = UIPasteboard.generalPasteboard;
return (__bridge_retained CFTypeRef)p.string; return (__bridge_retained CFTypeRef)p.string;
} }
#else
return nil;
#endif
} }
static void showTextInput(CFTypeRef viewRef) { static void showTextInput(CFTypeRef viewRef) {
@@ -80,27 +72,21 @@ import "C"
import ( import (
"image" "image"
"io"
"os"
"runtime" "runtime"
"runtime/cgo"
"runtime/debug" "runtime/debug"
"strings"
"time" "time"
"unicode/utf16" "unicode/utf16"
"unsafe" "unsafe"
"gioui.org/f32" "gioui.org/f32"
"gioui.org/io/event" "gioui.org/io/clipboard"
"gioui.org/io/key" "gioui.org/io/key"
"gioui.org/io/pointer" "gioui.org/io/pointer"
"gioui.org/io/system" "gioui.org/io/system"
"gioui.org/io/transfer"
"gioui.org/op"
"gioui.org/unit" "gioui.org/unit"
) )
type UIKitViewEvent struct { type ViewEvent struct {
// ViewController is a CFTypeRef for the UIViewController backing a Window. // ViewController is a CFTypeRef for the UIViewController backing a Window.
ViewController uintptr ViewController uintptr
} }
@@ -109,17 +95,18 @@ type window struct {
view C.CFTypeRef view C.CFTypeRef
w *callbacks w *callbacks
displayLink *displayLink displayLink *displayLink
loop *eventLoop
hidden bool visible bool
cursor pointer.Cursor cursor pointer.Cursor
config Config config Config
pointerMap []C.CFTypeRef pointerMap []C.CFTypeRef
} }
var mainWindow = newWindowRendezvous() var mainWindow = newWindowRendezvous()
var views = make(map[C.CFTypeRef]*window)
func init() { func init() {
// Darwin requires UI operations happen on the main thread only. // Darwin requires UI operations happen on the main thread only.
runtime.LockOSThread() runtime.LockOSThread()
@@ -127,59 +114,55 @@ func init() {
//export onCreate //export onCreate
func onCreate(view, controller C.CFTypeRef) { func onCreate(view, controller C.CFTypeRef) {
wopts := <-mainWindow.out
w := &window{ w := &window{
view: view, view: view,
w: wopts.window,
} }
w.loop = newEventLoop(w.w, w.wakeup)
w.w.SetDriver(w)
mainWindow.windows <- struct{}{}
dl, err := newDisplayLink(func() { dl, err := newDisplayLink(func() {
w.draw(false) w.draw(false)
}) })
if err != nil { if err != nil {
w.w.ProcessEvent(DestroyEvent{Err: err}) panic(err)
return
} }
w.displayLink = dl w.displayLink = dl
C.gio_viewSetHandle(view, C.uintptr_t(cgo.NewHandle(w))) wopts := <-mainWindow.out
w.w = wopts.window
w.w.SetDriver(w)
views[view] = w
w.Configure(wopts.options) w.Configure(wopts.options)
w.ProcessEvent(UIKitViewEvent{ViewController: uintptr(controller)}) w.w.Event(system.StageEvent{Stage: system.StagePaused})
} w.w.Event(ViewEvent{ViewController: uintptr(controller)})
func viewFor(h C.uintptr_t) *window {
return cgo.Handle(h).Value().(*window)
} }
//export gio_onDraw //export gio_onDraw
func gio_onDraw(h C.uintptr_t) { func gio_onDraw(view C.CFTypeRef) {
w := viewFor(h) w := views[view]
w.draw(true) w.draw(true)
} }
func (w *window) draw(sync bool) { func (w *window) draw(sync bool) {
if w.hidden {
return
}
params := C.viewDrawParams(w.view) params := C.viewDrawParams(w.view)
if params.width == 0 || params.height == 0 { if params.width == 0 || params.height == 0 {
return return
} }
wasVisible := w.visible
w.visible = true
if !wasVisible {
w.w.Event(system.StageEvent{Stage: system.StageRunning})
}
const inchPrDp = 1.0 / 163 const inchPrDp = 1.0 / 163
m := unit.Metric{ m := unit.Metric{
PxPerDp: float32(params.dpi) * inchPrDp, PxPerDp: float32(params.dpi) * inchPrDp,
PxPerSp: float32(params.sdpi) * inchPrDp, PxPerSp: float32(params.sdpi) * inchPrDp,
} }
dppp := unit.Dp(1. / m.PxPerDp) dppp := unit.Dp(1. / m.PxPerDp)
w.ProcessEvent(frameEvent{ w.w.Event(frameEvent{
FrameEvent: FrameEvent{ FrameEvent: system.FrameEvent{
Now: time.Now(), Now: time.Now(),
Size: image.Point{ Size: image.Point{
X: int(params.width + .5), X: int(params.width + .5),
Y: int(params.height + .5), Y: int(params.height + .5),
}, },
Insets: Insets{ Insets: system.Insets{
Top: unit.Dp(params.top) * dppp, Top: unit.Dp(params.top) * dppp,
Bottom: unit.Dp(params.bottom) * dppp, Bottom: unit.Dp(params.bottom) * dppp,
Left: unit.Dp(params.left) * dppp, Left: unit.Dp(params.left) * dppp,
@@ -192,34 +175,26 @@ func (w *window) draw(sync bool) {
} }
//export onStop //export onStop
func onStop(h C.uintptr_t) { func onStop(view C.CFTypeRef) {
w := viewFor(h) w := views[view]
w.hidden = true w.visible = false
} w.w.Event(system.StageEvent{Stage: system.StagePaused})
//export onStart
func onStart(h C.uintptr_t) {
w := viewFor(h)
w.hidden = false
w.draw(true)
} }
//export onDestroy //export onDestroy
func onDestroy(h C.uintptr_t) { func onDestroy(view C.CFTypeRef) {
w := viewFor(h) w := views[view]
w.ProcessEvent(UIKitViewEvent{}) delete(views, view)
w.ProcessEvent(DestroyEvent{}) w.w.Event(ViewEvent{})
w.w.Event(system.DestroyEvent{})
w.displayLink.Close() w.displayLink.Close()
w.displayLink = nil
cgo.Handle(h).Delete()
w.view = 0 w.view = 0
} }
//export onFocus //export onFocus
func onFocus(h C.uintptr_t, focus int) { func onFocus(view C.CFTypeRef, focus int) {
w := viewFor(h) w := views[view]
w.config.Focused = focus != 0 w.w.Event(key.FocusEvent{Focus: focus != 0})
w.ProcessEvent(ConfigEvent{Config: w.config})
} }
//export onLowMemory //export onLowMemory
@@ -229,38 +204,38 @@ func onLowMemory() {
} }
//export onUpArrow //export onUpArrow
func onUpArrow(h C.uintptr_t) { func onUpArrow(view C.CFTypeRef) {
viewFor(h).onKeyCommand(key.NameUpArrow) views[view].onKeyCommand(key.NameUpArrow)
} }
//export onDownArrow //export onDownArrow
func onDownArrow(h C.uintptr_t) { func onDownArrow(view C.CFTypeRef) {
viewFor(h).onKeyCommand(key.NameDownArrow) views[view].onKeyCommand(key.NameDownArrow)
} }
//export onLeftArrow //export onLeftArrow
func onLeftArrow(h C.uintptr_t) { func onLeftArrow(view C.CFTypeRef) {
viewFor(h).onKeyCommand(key.NameLeftArrow) views[view].onKeyCommand(key.NameLeftArrow)
} }
//export onRightArrow //export onRightArrow
func onRightArrow(h C.uintptr_t) { func onRightArrow(view C.CFTypeRef) {
viewFor(h).onKeyCommand(key.NameRightArrow) views[view].onKeyCommand(key.NameRightArrow)
} }
//export onDeleteBackward //export onDeleteBackward
func onDeleteBackward(h C.uintptr_t) { func onDeleteBackward(view C.CFTypeRef) {
viewFor(h).onKeyCommand(key.NameDeleteBackward) views[view].onKeyCommand(key.NameDeleteBackward)
} }
//export onText //export onText
func onText(h C.uintptr_t, str C.CFTypeRef) { func onText(view, str C.CFTypeRef) {
w := viewFor(h) w := views[view]
w.w.EditorInsert(nsstringToString(str)) w.w.EditorInsert(nsstringToString(str))
} }
//export onTouch //export onTouch
func onTouch(h C.uintptr_t, last C.int, touchRef C.CFTypeRef, phase C.NSInteger, x, y C.CGFloat, ti C.double) { func onTouch(last C.int, view, touchRef C.CFTypeRef, phase C.NSInteger, x, y C.CGFloat, ti C.double) {
var kind pointer.Kind var kind pointer.Kind
switch phase { switch phase {
case C.UITouchPhaseBegan: case C.UITouchPhaseBegan:
@@ -274,10 +249,10 @@ func onTouch(h C.uintptr_t, last C.int, touchRef C.CFTypeRef, phase C.NSInteger,
default: default:
return return
} }
w := viewFor(h) w := views[view]
t := time.Duration(float64(ti) * float64(time.Second)) t := time.Duration(float64(ti) * float64(time.Second))
p := f32.Point{X: float32(x), Y: float32(y)} p := f32.Point{X: float32(x), Y: float32(y)}
w.ProcessEvent(pointer.Event{ w.w.Event(pointer.Event{
Kind: kind, Kind: kind,
Source: pointer.Touch, Source: pointer.Touch,
PointerID: w.lookupTouch(last != 0, touchRef), PointerID: w.lookupTouch(last != 0, touchRef),
@@ -290,16 +265,11 @@ func (w *window) ReadClipboard() {
cstr := C.readClipboard() cstr := C.readClipboard()
defer C.CFRelease(cstr) defer C.CFRelease(cstr)
content := nsstringToString(cstr) content := nsstringToString(cstr)
w.ProcessEvent(transfer.DataEvent{ w.w.Event(clipboard.Event{Text: content})
Type: "application/text",
Open: func() io.ReadCloser {
return io.NopCloser(strings.NewReader(content))
},
})
} }
func (w *window) WriteClipboard(mime string, s []byte) { func (w *window) WriteClipboard(s string) {
u16 := utf16.Encode([]rune(string(s))) u16 := utf16.Encode([]rune(s))
var chars *C.unichar var chars *C.unichar
if len(u16) > 0 { if len(u16) > 0 {
chars = (*C.unichar)(unsafe.Pointer(&u16[0])) chars = (*C.unichar)(unsafe.Pointer(&u16[0]))
@@ -310,7 +280,7 @@ func (w *window) WriteClipboard(mime string, s []byte) {
func (w *window) Configure([]Option) { func (w *window) Configure([]Option) {
// Decorations are never disabled. // Decorations are never disabled.
w.config.Decorated = true w.config.Decorated = true
w.ProcessEvent(ConfigEvent{Config: w.config}) w.w.Event(ConfigEvent{Config: w.config})
} }
func (w *window) EditorStateChanged(old, new editorState) {} func (w *window) EditorStateChanged(old, new editorState) {}
@@ -318,6 +288,10 @@ func (w *window) EditorStateChanged(old, new editorState) {}
func (w *window) Perform(system.Action) {} func (w *window) Perform(system.Action) {}
func (w *window) SetAnimating(anim bool) { func (w *window) SetAnimating(anim bool) {
v := w.view
if v == 0 {
return
}
if anim { if anim {
w.displayLink.Start() w.displayLink.Start()
} else { } else {
@@ -329,8 +303,8 @@ func (w *window) SetCursor(cursor pointer.Cursor) {
w.cursor = windowSetCursor(w.cursor, cursor) w.cursor = windowSetCursor(w.cursor, cursor)
} }
func (w *window) onKeyCommand(name key.Name) { func (w *window) onKeyCommand(name string) {
w.ProcessEvent(key.Event{ w.w.Event(key.Event{
Name: name, Name: name,
}) })
} }
@@ -369,88 +343,17 @@ func (w *window) ShowTextInput(show bool) {
func (w *window) SetInputHint(_ key.InputHint) {} func (w *window) SetInputHint(_ key.InputHint) {}
func (w *window) ProcessEvent(e event.Event) { func newWindow(win *callbacks, options []Option) error {
w.w.ProcessEvent(e)
w.loop.FlushEvents()
}
func (w *window) Event() event.Event {
return w.loop.Event()
}
func (w *window) Invalidate() {
w.loop.Invalidate()
}
func (w *window) Run(f func()) {
w.loop.Run(f)
}
func (w *window) Frame(frame *op.Ops) {
w.loop.Frame(frame)
}
func newWindow(win *callbacks, options []Option) {
mainWindow.in <- windowAndConfig{win, options} mainWindow.in <- windowAndConfig{win, options}
<-mainWindow.windows return <-mainWindow.errs
} }
var mainMode = mainModeUndefined
const (
mainModeUndefined = iota
mainModeExe
mainModeLibrary
)
func osMain() { func osMain() {
switch mainMode {
case mainModeUndefined:
if !isMainThread() {
panic("app.Main must be run on the main goroutine")
}
mainMode = mainModeExe
var argv []*C.char
for _, arg := range os.Args {
a := C.CString(arg)
defer C.free(unsafe.Pointer(a))
argv = append(argv, a)
}
C.gio_applicationMain(C.int(len(argv)), unsafe.SliceData(argv))
case mainModeExe:
panic("app.Main may be called only once")
case mainModeLibrary:
// Do nothing, we're embedded as a library.
}
select {}
}
//export gio_onOpenURI
func gio_onOpenURI(uri C.CFTypeRef) {
evt, err := newURLEvent(nsstringToString(uri))
if err != nil {
return
}
processGlobalEvent(evt)
} }
//export gio_runMain //export gio_runMain
func gio_runMain() { func gio_runMain() {
if !isMainThread() { runMain()
panic("app.Main must be run on the main goroutine")
}
switch mainMode {
case mainModeUndefined:
mainMode = mainModeLibrary
runMain()
case mainModeExe:
// Do nothing, main has already been called.
}
} }
func (UIKitViewEvent) implementsViewEvent() {} func (_ ViewEvent) ImplementsEvent() {}
func (UIKitViewEvent) ImplementsEvent() {}
func (u UIKitViewEvent) Valid() bool {
return u != (UIKitViewEvent{})
}
+33 -102
View File
@@ -11,7 +11,6 @@
__attribute__ ((visibility ("hidden"))) Class gio_layerClass(void); __attribute__ ((visibility ("hidden"))) Class gio_layerClass(void);
@interface GioView: UIView <UIKeyInput> @interface GioView: UIView <UIKeyInput>
@property uintptr_t handle;
@end @end
@implementation GioViewController @implementation GioViewController
@@ -26,13 +25,12 @@ CGFloat _keyboardHeight;
self.view.layoutMargins = UIEdgeInsetsMake(0, 0, 0, 0); self.view.layoutMargins = UIEdgeInsetsMake(0, 0, 0, 0);
UIView *drawView = [[GioView alloc] initWithFrame:zeroFrame]; UIView *drawView = [[GioView alloc] initWithFrame:zeroFrame];
[self.view addSubview: drawView]; [self.view addSubview: drawView];
#if !TARGET_OS_TV #ifndef TARGET_OS_TV
drawView.multipleTouchEnabled = YES; drawView.multipleTouchEnabled = YES;
#endif #endif
drawView.preservesSuperviewLayoutMargins = YES; drawView.preservesSuperviewLayoutMargins = YES;
drawView.layoutMargins = UIEdgeInsetsMake(0, 0, 0, 0); drawView.layoutMargins = UIEdgeInsetsMake(0, 0, 0, 0);
onCreate((__bridge CFTypeRef)drawView, (__bridge CFTypeRef)self); onCreate((__bridge CFTypeRef)drawView, (__bridge CFTypeRef)self);
#if !TARGET_OS_TV
[[NSNotificationCenter defaultCenter] addObserver:self [[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillChange:) selector:@selector(keyboardWillChange:)
name:UIKeyboardWillShowNotification name:UIKeyboardWillShowNotification
@@ -45,7 +43,6 @@ CGFloat _keyboardHeight;
selector:@selector(keyboardWillHide:) selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification name:UIKeyboardWillHideNotification
object:nil]; object:nil];
#endif
[[NSNotificationCenter defaultCenter] addObserver: self [[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(applicationDidEnterBackground:) selector: @selector(applicationDidEnterBackground:)
name: UIApplicationDidEnterBackgroundNotification name: UIApplicationDidEnterBackgroundNotification
@@ -57,33 +54,33 @@ CGFloat _keyboardHeight;
} }
- (void)applicationWillEnterForeground:(UIApplication *)application { - (void)applicationWillEnterForeground:(UIApplication *)application {
GioView *view = (GioView *)self.view.subviews[0]; UIView *drawView = self.view.subviews[0];
if (view != nil) { if (drawView != nil) {
onStart(view.handle); gio_onDraw((__bridge CFTypeRef)drawView);
} }
} }
- (void)applicationDidEnterBackground:(UIApplication *)application { - (void)applicationDidEnterBackground:(UIApplication *)application {
GioView *view = (GioView *)self.view.subviews[0]; UIView *drawView = self.view.subviews[0];
if (view != nil) { if (drawView != nil) {
onStop(view.handle); onStop((__bridge CFTypeRef)drawView);
} }
} }
- (void)viewDidDisappear:(BOOL)animated { - (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated]; [super viewDidDisappear:animated];
GioView *view = (GioView *)self.view.subviews[0]; CFTypeRef viewRef = (__bridge CFTypeRef)self.view.subviews[0];
onDestroy(view.handle); onDestroy(viewRef);
} }
- (void)viewDidLayoutSubviews { - (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews]; [super viewDidLayoutSubviews];
GioView *view = (GioView *)self.view.subviews[0]; UIView *view = self.view.subviews[0];
CGRect frame = self.view.bounds; CGRect frame = self.view.bounds;
// Adjust view bounds to make room for the keyboard. // Adjust view bounds to make room for the keyboard.
frame.size.height -= _keyboardHeight; frame.size.height -= _keyboardHeight;
view.frame = frame; view.frame = frame;
gio_onDraw(view.handle); gio_onDraw((__bridge CFTypeRef)view);
} }
- (void)didReceiveMemoryWarning { - (void)didReceiveMemoryWarning {
@@ -91,7 +88,6 @@ CGFloat _keyboardHeight;
[super didReceiveMemoryWarning]; [super didReceiveMemoryWarning];
} }
#if !TARGET_OS_TV
- (void)keyboardWillChange:(NSNotification *)note { - (void)keyboardWillChange:(NSNotification *)note {
NSDictionary *userInfo = note.userInfo; NSDictionary *userInfo = note.userInfo;
CGRect f = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; CGRect f = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
@@ -103,13 +99,13 @@ CGFloat _keyboardHeight;
_keyboardHeight = 0.0; _keyboardHeight = 0.0;
[self.view setNeedsLayout]; [self.view setNeedsLayout];
} }
#endif
@end @end
static void handleTouches(int last, GioView *view, NSSet<UITouch *> *touches, UIEvent *event) { static void handleTouches(int last, UIView *view, NSSet<UITouch *> *touches, UIEvent *event) {
CGFloat scale = view.contentScaleFactor; CGFloat scale = view.contentScaleFactor;
NSUInteger i = 0; NSUInteger i = 0;
NSUInteger n = [touches count]; NSUInteger n = [touches count];
CFTypeRef viewRef = (__bridge CFTypeRef)view;
for (UITouch *touch in touches) { for (UITouch *touch in touches) {
CFTypeRef touchRef = (__bridge CFTypeRef)touch; CFTypeRef touchRef = (__bridge CFTypeRef)touch;
i++; i++;
@@ -120,7 +116,7 @@ static void handleTouches(int last, GioView *view, NSSet<UITouch *> *touches, UI
CGPoint loc = [coalescedTouch locationInView:view]; CGPoint loc = [coalescedTouch locationInView:view];
j++; j++;
int lastTouch = last && i == n && j == m; int lastTouch = last && i == n && j == m;
onTouch(view.handle, lastTouch, touchRef, touch.phase, loc.x*scale, loc.y*scale, [coalescedTouch timestamp]); onTouch(lastTouch, viewRef, touchRef, touch.phase, loc.x*scale, loc.y*scale, [coalescedTouch timestamp]);
} }
} }
} }
@@ -134,70 +130,34 @@ NSArray<UIKeyCommand *> *_keyCommands;
return gio_layerClass(); return gio_layerClass();
} }
- (void)willMoveToWindow:(UIWindow *)newWindow { - (void)willMoveToWindow:(UIWindow *)newWindow {
self.contentScaleFactor = newWindow.screen.nativeScale;
if (@available(iOS 13.0, *)) {
[self registerSceneNotifications:newWindow];
}else{
[self registerWindowNotifications:newWindow];
}
}
- (void)registerSceneNotifications:(UIWindow *)newWindow {
if (self.window != nil) {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UISceneDidActivateNotification
object:self.window.windowScene];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UISceneWillDeactivateNotification
object:self.window.windowScene];
}
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(onSceneDidActivate:)
name:UISceneDidActivateNotification
object:newWindow.windowScene];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(onSceneWillDeactivate:)
name:UISceneWillDeactivateNotification
object:newWindow.windowScene];
}
- (void)onSceneDidActivate:(NSNotification *)note API_AVAILABLE(ios(13.0)){
onFocus(self.handle, YES);
}
- (void)onSceneWillDeactivate:(NSNotification *)note API_AVAILABLE(ios(13.0)){
onFocus(self.handle, NO);
}
- (void)registerWindowNotifications:(UIWindow *)newWindow {
if (self.window != nil) { if (self.window != nil) {
[[NSNotificationCenter defaultCenter] removeObserver:self [[NSNotificationCenter defaultCenter] removeObserver:self
name:UIWindowDidBecomeKeyNotification name:UIWindowDidBecomeKeyNotification
object:self.window]; object:self.window];
[[NSNotificationCenter defaultCenter] removeObserver:self [[NSNotificationCenter defaultCenter] removeObserver:self
name:UIWindowDidResignKeyNotification name:UIWindowDidResignKeyNotification
object:self.window]; object:self.window];
} }
self.contentScaleFactor = newWindow.screen.nativeScale;
[[NSNotificationCenter defaultCenter] addObserver:self [[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(onWindowDidBecomeKey:) selector:@selector(onWindowDidBecomeKey:)
name:UIWindowDidBecomeKeyNotification name:UIWindowDidBecomeKeyNotification
object:newWindow]; object:newWindow];
[[NSNotificationCenter defaultCenter] addObserver:self [[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(onWindowDidResignKey:) selector:@selector(onWindowDidResignKey:)
name:UIWindowDidResignKeyNotification name:UIWindowDidResignKeyNotification
object:newWindow]; object:newWindow];
} }
- (void)onWindowDidBecomeKey:(NSNotification *)note { - (void)onWindowDidBecomeKey:(NSNotification *)note {
if (self.isFirstResponder) { if (self.isFirstResponder) {
onFocus(self.handle, YES); onFocus((__bridge CFTypeRef)self, YES);
} }
} }
- (void)onWindowDidResignKey:(NSNotification *)note { - (void)onWindowDidResignKey:(NSNotification *)note {
if (self.isFirstResponder) { if (self.isFirstResponder) {
onFocus(self.handle, NO); onFocus((__bridge CFTypeRef)self, NO);
} }
} }
@@ -218,7 +178,7 @@ NSArray<UIKeyCommand *> *_keyCommands;
} }
- (void)insertText:(NSString *)text { - (void)insertText:(NSString *)text {
onText(self.handle, (__bridge CFTypeRef)text); onText((__bridge CFTypeRef)self, (__bridge CFTypeRef)text);
} }
- (BOOL)canBecomeFirstResponder { - (BOOL)canBecomeFirstResponder {
@@ -230,23 +190,23 @@ NSArray<UIKeyCommand *> *_keyCommands;
} }
- (void)deleteBackward { - (void)deleteBackward {
onDeleteBackward(self.handle); onDeleteBackward((__bridge CFTypeRef)self);
} }
- (void)onUpArrow { - (void)onUpArrow {
onUpArrow(self.handle); onUpArrow((__bridge CFTypeRef)self);
} }
- (void)onDownArrow { - (void)onDownArrow {
onDownArrow(self.handle); onDownArrow((__bridge CFTypeRef)self);
} }
- (void)onLeftArrow { - (void)onLeftArrow {
onLeftArrow(self.handle); onLeftArrow((__bridge CFTypeRef)self);
} }
- (void)onRightArrow { - (void)onRightArrow {
onRightArrow(self.handle); onRightArrow((__bridge CFTypeRef)self);
} }
- (NSArray<UIKeyCommand *> *)keyCommands { - (NSArray<UIKeyCommand *> *)keyCommands {
@@ -311,32 +271,3 @@ void gio_showCursor() {
void gio_setCursor(NSUInteger curID) { void gio_setCursor(NSUInteger curID) {
// Not supported. // Not supported.
} }
void gio_viewSetHandle(CFTypeRef viewRef, uintptr_t handle) {
GioView *v = (__bridge GioView *)viewRef;
v.handle = handle;
}
@interface _gioAppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end
@implementation _gioAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
GioViewController *controller = [[GioViewController alloc] initWithNibName:nil bundle:nil];
self.window.rootViewController = controller;
[self.window makeKeyAndVisible];
return YES;
}
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
gio_onOpenURI((__bridge CFTypeRef)url.absoluteString);
return YES;
}
@end
int gio_applicationMain(int argc, char *argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([_gioAppDelegate class]));
}
}
+110 -447
View File
@@ -6,7 +6,6 @@ import (
"fmt" "fmt"
"image" "image"
"image/color" "image/color"
"io"
"strings" "strings"
"syscall/js" "syscall/js"
"time" "time"
@@ -14,18 +13,16 @@ import (
"unicode/utf8" "unicode/utf8"
"gioui.org/internal/f32color" "gioui.org/internal/f32color"
"gioui.org/op"
"gioui.org/f32" "gioui.org/f32"
"gioui.org/io/event" "gioui.org/io/clipboard"
"gioui.org/io/key" "gioui.org/io/key"
"gioui.org/io/pointer" "gioui.org/io/pointer"
"gioui.org/io/system" "gioui.org/io/system"
"gioui.org/io/transfer"
"gioui.org/unit" "gioui.org/unit"
) )
type JSViewEvent struct { type ViewEvent struct {
Element js.Value Element js.Value
} }
@@ -53,10 +50,12 @@ type window struct {
screenOrientation js.Value screenOrientation js.Value
cleanfuncs []func() cleanfuncs []func()
touches []js.Value touches []js.Value
composing int composing bool
lastCursor int
requestFocus bool requestFocus bool
chanAnimation chan struct{}
chanRedraw chan struct{}
config Config config Config
inset f32.Point inset f32.Point
scale float32 scale float32
@@ -69,7 +68,7 @@ type window struct {
contextStatus contextStatus contextStatus contextStatus
} }
func newWindow(win *callbacks, options []Option) { func newWindow(win *callbacks, options []Option) error {
doc := js.Global().Get("document") doc := js.Global().Get("document")
cont := getContainer(doc) cont := getContainer(doc)
cnv := createCanvas(doc) cnv := createCanvas(doc)
@@ -84,10 +83,7 @@ func newWindow(win *callbacks, options []Option) {
head: doc.Get("head"), head: doc.Get("head"),
clipboard: js.Global().Get("navigator").Get("clipboard"), clipboard: js.Global().Get("navigator").Get("clipboard"),
wakeups: make(chan struct{}, 1), wakeups: make(chan struct{}, 1),
w: win,
composing: -1,
} }
w.w.SetDriver(w)
w.requestAnimationFrame = w.window.Get("requestAnimationFrame") w.requestAnimationFrame = w.window.Get("requestAnimationFrame")
w.browserHistory = w.window.Get("history") w.browserHistory = w.window.Get("history")
w.visualViewport = w.window.Get("visualViewport") w.visualViewport = w.window.Get("visualViewport")
@@ -97,28 +93,42 @@ func newWindow(win *callbacks, options []Option) {
if screen := w.window.Get("screen"); screen.Truthy() { if screen := w.window.Get("screen"); screen.Truthy() {
w.screenOrientation = screen.Get("orientation") w.screenOrientation = screen.Get("orientation")
} }
w.chanAnimation = make(chan struct{}, 1)
w.chanRedraw = make(chan struct{}, 1)
w.redraw = w.funcOf(func(this js.Value, args []js.Value) interface{} { w.redraw = w.funcOf(func(this js.Value, args []js.Value) interface{} {
w.draw(false) w.chanAnimation <- struct{}{}
return nil return nil
}) })
w.clipboardCallback = w.funcOf(func(this js.Value, args []js.Value) interface{} { w.clipboardCallback = w.funcOf(func(this js.Value, args []js.Value) interface{} {
content := args[0].String() content := args[0].String()
w.processEvent(transfer.DataEvent{ go win.Event(clipboard.Event{Text: content})
Type: "application/text",
Open: func() io.ReadCloser {
return io.NopCloser(strings.NewReader(content))
},
})
return nil return nil
}) })
w.addEventListeners() w.addEventListeners()
w.addHistory() w.addHistory()
w.w = win
w.Configure(options) go func() {
w.blur() defer w.cleanup()
w.processEvent(JSViewEvent{Element: cont}) w.w.SetDriver(w)
w.resize() w.Configure(options)
w.draw(true) w.blur()
w.w.Event(ViewEvent{Element: cont})
w.w.Event(system.StageEvent{Stage: system.StageRunning})
w.resize()
w.draw(true)
for {
select {
case <-w.wakeups:
w.w.Event(wakeupEvent{})
case <-w.chanAnimation:
w.animCallback()
case <-w.chanRedraw:
w.draw(true)
}
}
}()
return nil
} }
func getContainer(doc js.Value) js.Value { func getContainer(doc js.Value) js.Value {
@@ -132,10 +142,8 @@ func getContainer(doc js.Value) js.Value {
} }
func createTextArea(doc js.Value) js.Value { func createTextArea(doc js.Value) js.Value {
tarea := doc.Call("createElement", "textarea") tarea := doc.Call("createElement", "input")
style := tarea.Get("style") style := tarea.Get("style")
// Position absolute so left/top coordinates actually place the element
style.Set("position", "absolute")
style.Set("width", "1px") style.Set("width", "1px")
style.Set("height", "1px") style.Set("height", "1px")
style.Set("opacity", "0") style.Set("opacity", "0")
@@ -145,12 +153,6 @@ func createTextArea(doc js.Value) js.Value {
tarea.Set("autocorrect", "off") tarea.Set("autocorrect", "off")
tarea.Set("autocapitalize", "off") tarea.Set("autocapitalize", "off")
tarea.Set("spellcheck", false) tarea.Set("spellcheck", false)
// Enable multiline text input for better composition support on some browsers.
tarea.Set("rows", 1)
style.Set("resize", "none")
style.Set("overflow", "hidden")
style.Set("white-space", "pre-wrap")
style.Set("word-break", "normal")
return tarea return tarea
} }
@@ -186,12 +188,12 @@ func (w *window) addEventListeners() {
w.cnv.Set("width", 0) w.cnv.Set("width", 0)
w.cnv.Set("height", 0) w.cnv.Set("height", 0)
w.resize() w.resize()
w.draw(true) w.requestRedraw()
return nil return nil
}) })
w.addEventListener(w.visualViewport, "resize", func(this js.Value, args []js.Value) interface{} { w.addEventListener(w.visualViewport, "resize", func(this js.Value, args []js.Value) interface{} {
w.resize() w.resize()
w.draw(true) w.requestRedraw()
return nil return nil
}) })
w.addEventListener(w.window, "contextmenu", func(this js.Value, args []js.Value) interface{} { w.addEventListener(w.window, "contextmenu", func(this js.Value, args []js.Value) interface{} {
@@ -199,11 +201,22 @@ func (w *window) addEventListeners() {
return nil return nil
}) })
w.addEventListener(w.window, "popstate", func(this js.Value, args []js.Value) interface{} { w.addEventListener(w.window, "popstate", func(this js.Value, args []js.Value) interface{} {
if w.processEvent(key.Event{Name: key.NameBack}) { if w.w.Event(key.Event{Name: key.NameBack}) {
return w.browserHistory.Call("forward") return w.browserHistory.Call("forward")
} }
return w.browserHistory.Call("back") return w.browserHistory.Call("back")
}) })
w.addEventListener(w.document, "visibilitychange", func(this js.Value, args []js.Value) interface{} {
ev := system.StageEvent{}
switch w.document.Get("visibilityState").String() {
case "hidden", "prerender", "unloaded":
ev.Stage = system.StagePaused
default:
ev.Stage = system.StageRunning
}
w.w.Event(ev)
return nil
})
w.addEventListener(w.cnv, "mousemove", func(this js.Value, args []js.Value) interface{} { w.addEventListener(w.cnv, "mousemove", func(this js.Value, args []js.Value) interface{} {
w.pointerEvent(pointer.Move, 0, 0, args[0]) w.pointerEvent(pointer.Move, 0, 0, args[0])
return nil return nil
@@ -261,28 +274,18 @@ func (w *window) addEventListeners() {
w.touches[i] = js.Null() w.touches[i] = js.Null()
} }
w.touches = w.touches[:0] w.touches = w.touches[:0]
w.processEvent(pointer.Event{ w.w.Event(pointer.Event{
Kind: pointer.Cancel, Kind: pointer.Cancel,
Source: pointer.Touch, Source: pointer.Touch,
}) })
return nil return nil
}) })
w.addEventListener(w.tarea, "focus", func(this js.Value, args []js.Value) interface{} { w.addEventListener(w.tarea, "focus", func(this js.Value, args []js.Value) interface{} {
w.config.Focused = true w.w.Event(key.FocusEvent{Focus: true})
w.processEvent(ConfigEvent{Config: w.config})
return nil return nil
}) })
w.addEventListener(w.tarea, "blur", func(this js.Value, args []js.Value) interface{} { w.addEventListener(w.tarea, "blur", func(this js.Value, args []js.Value) interface{} {
if w.composing != -1 { w.w.Event(key.FocusEvent{Focus: false})
// If we're composing, try to cancel.
// On Javascript is not possible to cancel the composition once started.
w.w.SetComposingRegion(key.Range{Start: -1, End: -1})
w.composing = -1
}
w.config.Focused = false
w.lastCursor = 0 // Reset cursor tracking on blur
w.processEvent(ConfigEvent{Config: w.config})
w.blur() w.blur()
return nil return nil
}) })
@@ -295,205 +298,19 @@ func (w *window) addEventListeners() {
return nil return nil
}) })
w.addEventListener(w.tarea, "compositionstart", func(this js.Value, args []js.Value) interface{} { w.addEventListener(w.tarea, "compositionstart", func(this js.Value, args []js.Value) interface{} {
st := w.w.EditorState() w.composing = true
sel := st.Selection.Range
if sel.Start == -1 {
sel.Start = 0
sel.End = 0
}
w.w.SetEditorSnippet(key.Range{Start: sel.Start, End: sel.End})
w.composing = sel.Start
return nil return nil
}) })
w.addEventListener(w.tarea, "compositionend", func(this js.Value, args []js.Value) interface{} { w.addEventListener(w.tarea, "compositionend", func(this js.Value, args []js.Value) interface{} {
finalText := w.tarea.Get("value").String() w.composing = false
w.flushInput()
if w.composing != -1 && finalText != "" {
// Replace the entire composition range with the final text.
compEnd := w.composing + utf8.RuneCountInString(finalText)
replaceRange := key.Range{Start: w.composing, End: compEnd}
w.w.EditorReplace(replaceRange, finalText)
w.w.SetComposingRegion(key.Range{Start: -1, End: -1})
// Position cursor after the final composition text.
newEnd := w.composing + utf8.RuneCountInString(finalText)
w.w.SetEditorSelection(key.Range{Start: newEnd, End: newEnd})
}
w.composing = -1
w.tarea.Set("value", "")
return nil return nil
}) })
w.addEventListener(w.tarea, "input", func(this js.Value, args []js.Value) interface{} { w.addEventListener(w.tarea, "input", func(this js.Value, args []js.Value) interface{} {
e := args[0] if w.composing {
inputType := e.Get("inputType").String() return nil
dataVal := e.Get("data")
var data string
if dataVal.Truthy() {
data = dataVal.String()
} }
w.flushInput()
// Get the current textarea value.
tareaValue := w.tarea.Get("value").String()
st := w.w.EditorState()
sel := st.Selection.Range
var absStart, absEnd int
snippetStart := st.Snippet.Range.Start
snippetEnd := st.Snippet.Range.End
cursorPos := sel.Start
selectionEnd := sel.End
if cursorPos < 0 {
cursorPos = 0
selectionEnd = 0
}
// Check if we need to expand the snippet to include the range.
if st.Snippet.Range.Start == 0 && st.Snippet.Range.End == 0 && tareaValue != "" {
// Empty snippet - set it to include the selection/cursor.
w.w.SetEditorSnippet(key.Range{Start: cursorPos, End: selectionEnd})
absStart = cursorPos
absEnd = selectionEnd
} else if cursorPos < snippetStart || selectionEnd > snippetEnd {
// Selection is outside the snippet
newStart := snippetStart
newEnd := snippetEnd
if cursorPos < newStart {
newStart = cursorPos
}
if selectionEnd > newEnd {
newEnd = selectionEnd
}
w.w.SetEditorSnippet(key.Range{Start: newStart, End: newEnd})
// Refresh state after snippet update.
st = w.w.EditorState()
// Use the selection range directly.
absStart = cursorPos
absEnd = selectionEnd
} else {
// Selection is within snippet to absolute positions.
absStart = cursorPos
absEnd = selectionEnd
}
switch inputType {
case "insertCompositionText":
if w.composing == -1 {
break
}
compEnd := absEnd
if compEnd < w.composing {
compEnd = w.composing
}
replaceRange := key.Range{Start: w.composing, End: compEnd}
w.w.EditorReplace(replaceRange, data)
newEnd := w.composing + utf8.RuneCountInString(data)
w.w.SetComposingRegion(key.Range{Start: w.composing, End: newEnd})
w.w.SetEditorSelection(key.Range{Start: newEnd, End: newEnd})
case "deleteContentBackward", "deleteContentForward", "deleteByCut":
if w.composing != -1 {
compEnd := w.composing + utf8.RuneCountInString(tareaValue)
replaceRange := key.Range{Start: w.composing, End: compEnd}
w.w.EditorReplace(replaceRange, tareaValue)
newEnd := w.composing + utf8.RuneCountInString(tareaValue)
w.w.SetComposingRegion(key.Range{Start: w.composing, End: newEnd})
w.w.SetEditorSelection(key.Range{Start: newEnd, End: newEnd})
} else {
replaceRange := key.Range{Start: absStart, End: absEnd}
w.w.EditorReplace(replaceRange, "")
w.w.SetEditorSelection(key.Range{Start: absStart, End: absStart})
}
case "insertReplacementText":
if w.composing != -1 {
// During composition, replace the entire composition.
compEnd := w.composing + utf8.RuneCountInString(data)
replaceRange := key.Range{Start: w.composing, End: compEnd}
w.w.EditorReplace(replaceRange, data)
newEnd := w.composing + utf8.RuneCountInString(data)
w.w.SetComposingRegion(key.Range{Start: -1, End: -1})
w.w.SetEditorSelection(key.Range{Start: newEnd, End: newEnd})
w.composing = -1
w.lastCursor = newEnd
} else {
// Safari sends "insertReplacementText" for autocorrect, but the cursor is at the end of the word, so we need to find the word start.
insertLen := utf8.RuneCountInString(data)
wordStart := absStart
if absStart > snippetStart {
relPos := absStart - snippetStart
snippetRunes := []rune(st.Snippet.Text)
for i := relPos - 1; i >= 0; i-- {
if i >= len(snippetRunes) {
continue
}
r := snippetRunes[i]
if r == ' ' || r == '\t' || r == '\n' || r == '\r' {
break
}
wordStart = snippetStart + i
}
}
replaceRange := key.Range{Start: wordStart, End: absStart}
w.w.EditorReplace(replaceRange, data)
newCursor := wordStart + insertLen
w.w.SetEditorSelection(key.Range{Start: newCursor, End: newCursor})
w.lastCursor = newCursor
}
case "insertText":
if w.composing != -1 {
compEnd := w.composing + utf8.RuneCountInString(data)
replaceRange := key.Range{Start: w.composing, End: compEnd}
w.w.EditorReplace(replaceRange, data)
newEnd := w.composing + utf8.RuneCountInString(data)
w.w.SetComposingRegion(key.Range{Start: -1, End: -1})
w.w.SetEditorSelection(key.Range{Start: newEnd, End: newEnd})
w.composing = -1
w.lastCursor = newEnd
} else {
insertLen := utf8.RuneCountInString(data)
replaceRange := key.Range{Start: absStart, End: absStart}
if absStart != absEnd {
replaceRange = key.Range{Start: absStart, End: absEnd}
}
newCursor := replaceRange.Start + insertLen
w.w.EditorReplace(replaceRange, data)
w.w.SetEditorSelection(key.Range{Start: newCursor, End: newCursor})
w.lastCursor = newCursor
}
default: // paste and other input types
if w.composing != -1 {
compEnd := w.composing + utf8.RuneCountInString(tareaValue)
replaceRange := key.Range{Start: w.composing, End: compEnd}
w.w.EditorReplace(replaceRange, tareaValue)
newEnd := w.composing + utf8.RuneCountInString(tareaValue)
w.w.SetComposingRegion(key.Range{Start: w.composing, End: newEnd})
w.w.SetEditorSelection(key.Range{Start: newEnd, End: newEnd})
} else {
replaceRange := key.Range{Start: absStart, End: absEnd}
w.w.EditorReplace(replaceRange, tareaValue)
newCursor := absStart + utf8.RuneCountInString(tareaValue)
w.w.SetEditorSelection(key.Range{Start: newCursor, End: newCursor})
}
}
return nil return nil
}) })
w.addEventListener(w.tarea, "paste", func(this js.Value, args []js.Value) interface{} { w.addEventListener(w.tarea, "paste", func(this js.Value, args []js.Value) interface{} {
@@ -510,6 +327,12 @@ func (w *window) addHistory() {
w.browserHistory.Call("pushState", nil, nil, w.window.Get("location").Get("href")) w.browserHistory.Call("pushState", nil, nil, w.window.Get("location").Get("href"))
} }
func (w *window) flushInput() {
val := w.tarea.Get("value").String()
w.tarea.Set("value", "")
w.w.EditorInsert(string(val))
}
func (w *window) blur() { func (w *window) blur() {
w.tarea.Call("blur") w.tarea.Call("blur")
w.requestFocus = false w.requestFocus = false
@@ -541,98 +364,20 @@ func (w *window) keyboard(hint key.InputHint) {
m = "text" m = "text"
} }
w.tarea.Set("inputMode", m) w.tarea.Set("inputMode", m)
// Update autocomplete / autocorrect attributes.
var autocomplete, autocorrect, autocapitalize string
var spellcheck bool
switch hint {
case key.HintAny, key.HintText:
autocomplete, autocorrect, autocapitalize, spellcheck = "on", "on", "on", true
case key.HintEmail:
autocomplete, autocorrect, autocapitalize, spellcheck = "email", "off", "off", false
case key.HintURL:
autocomplete, autocorrect, autocapitalize, spellcheck = "url", "off", "off", false
case key.HintTelephone:
autocomplete, autocorrect, autocapitalize, spellcheck = "tel", "off", "off", false
case key.HintPassword:
autocomplete, autocorrect, autocapitalize, spellcheck = "current-password", "off", "off", false
default: // key.HintNumeric and others
autocomplete, autocorrect, autocapitalize, spellcheck = "off", "off", "off", false
}
w.tarea.Set("autocomplete", autocomplete)
w.tarea.Set("autocorrect", autocorrect)
w.tarea.Set("autocapitalize", autocapitalize)
w.tarea.Set("spellcheck", spellcheck)
} }
func (w *window) keyEvent(e js.Value, ks key.State) { func (w *window) keyEvent(e js.Value, ks key.State) {
k := e.Get("key").String() k := e.Get("key").String()
if n, ok := translateKey(k); ok { if n, ok := translateKey(k); ok {
if ks == key.Press {
isMod := n == key.NameAlt || n == key.NameCommand || n == key.NameCtrl || n == key.NameShift || n == key.NameSuper
isFunc := n == key.NameUpArrow || n == key.NameDownArrow || n == key.NameLeftArrow || n == key.NameRightArrow ||
n == key.NamePageUp || n == key.NamePageDown || n == key.NameHome || n == key.NameEnd ||
n == key.NameEscape || n == key.NameReturn || n == key.NameEnter || n == key.NameTab ||
n == key.NameDeleteBackward || n == key.NameDeleteForward
if isMod || isFunc {
// Gio will request the browser to change the selection/carret position natively.
e.Call("preventDefault")
}
}
cmd := key.Event{ cmd := key.Event{
Name: n, Name: n,
Modifiers: modifiersFor(e), Modifiers: modifiersFor(e),
State: ks, State: ks,
} }
w.processEvent(cmd) w.w.Event(cmd)
} }
} }
func (w *window) ProcessEvent(e event.Event) {
w.processEvent(e)
}
func (w *window) processEvent(e event.Event) bool {
if !w.w.ProcessEvent(e) {
return false
}
select {
case w.wakeups <- struct{}{}:
default:
}
return true
}
func (w *window) Event() event.Event {
for {
evt, ok := w.w.nextEvent()
if ok {
if _, destroy := evt.(DestroyEvent); destroy {
w.cleanup()
}
return evt
}
<-w.wakeups
}
}
func (w *window) Invalidate() {
w.w.Invalidate()
}
func (w *window) Run(f func()) {
f()
}
func (w *window) Frame(frame *op.Ops) {
w.w.ProcessFrame(frame, nil)
}
// modifiersFor returns the modifier set for a DOM MouseEvent or // modifiersFor returns the modifier set for a DOM MouseEvent or
// KeyEvent. // KeyEvent.
func modifiersFor(e js.Value) key.Modifiers { func modifiersFor(e js.Value) key.Modifiers {
@@ -650,12 +395,6 @@ func modifiersFor(e js.Value) key.Modifiers {
if e.Call("getModifierState", "Shift").Bool() { if e.Call("getModifierState", "Shift").Bool() {
mods |= key.ModShift mods |= key.ModShift
} }
if e.Call("getModifierState", "Meta").Bool() {
mods |= key.ModCommand
}
if e.Call("getModifierState", "OS").Bool() {
mods |= key.ModSuper
}
return mods return mods
} }
@@ -676,9 +415,6 @@ func (w *window) touchEvent(kind pointer.Kind, e js.Value) {
if e.Get("ctrlKey").Bool() { if e.Get("ctrlKey").Bool() {
mods |= key.ModCtrl mods |= key.ModCtrl
} }
if e.Get("metaKey").Bool() {
mods |= key.ModCommand
}
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
touch := changedTouches.Index(i) touch := changedTouches.Index(i)
pid := w.touchIDFor(touch) pid := w.touchIDFor(touch)
@@ -689,7 +425,7 @@ func (w *window) touchEvent(kind pointer.Kind, e js.Value) {
X: float32(x) * scale, X: float32(x) * scale,
Y: float32(y) * scale, Y: float32(y) * scale,
} }
w.processEvent(pointer.Event{ w.w.Event(pointer.Event{
Kind: kind, Kind: kind,
Source: pointer.Touch, Source: pointer.Touch,
Position: pos, Position: pos,
@@ -739,7 +475,7 @@ func (w *window) pointerEvent(kind pointer.Kind, dx, dy float32, e js.Value) {
if jbtns&4 != 0 { if jbtns&4 != 0 {
btns |= pointer.ButtonTertiary btns |= pointer.ButtonTertiary
} }
w.processEvent(pointer.Event{ w.w.Event(pointer.Event{
Kind: kind, Kind: kind,
Source: pointer.Mouse, Source: pointer.Mouse,
Buttons: btns, Buttons: btns,
@@ -766,91 +502,19 @@ func (w *window) funcOf(f func(this js.Value, args []js.Value) interface{}) js.F
return jsf return jsf
} }
func (w *window) EditorStateChanged(old, new editorState) { func (w *window) animCallback() {
if w.composing != -1 { anim := w.animating
// Do not interfere with browser state while composing. w.animRequested = anim
// On Javascript is not possible to cancel the composition once started! if anim {
return w.requestAnimationFrame.Invoke(w.redraw)
} }
if anim {
// Update textarea value to match the snippet. w.draw(false)
if old.Snippet != new.Snippet {
w.tarea.Set("value", new.Snippet.Text)
}
// Update selection to match Gio's selection.
if old.Selection.Range != new.Selection.Range || old.Snippet != new.Snippet {
if new.Selection.Range.Start != -1 && new.Selection.Range.End != -1 {
// Calculate selection positions relative to snippet start.
// The textarea contains only the snippet text.
snippetStart := new.Snippet.Range.Start
snippetEnd := new.Snippet.Range.End
selStart := new.Selection.Range.Start
selEnd := new.Selection.Range.End
if selStart < snippetStart {
selStart = snippetStart
}
if selStart > snippetEnd {
selStart = snippetEnd
}
if selEnd < snippetStart {
selEnd = snippetStart
}
if selEnd > snippetEnd {
selEnd = snippetEnd
}
// Convert absolute rune positions to UTF-16 positions for the textarea.
startUTF16 := new.UTF16Index(selStart)
endUTF16 := new.UTF16Index(selEnd)
// Convert to snippet-relative UTF-16 positions.
snippetStartUTF16 := new.UTF16Index(snippetStart)
start := startUTF16 - snippetStartUTF16
end := endUTF16 - snippetStartUTF16
if start < 0 {
start = 0
}
if end < 0 {
end = 0
}
// Calculate max UTF-16 length of snippet text.
textLen := new.UTF16Index(snippetEnd) - snippetStartUTF16
if start > textLen {
start = textLen
}
if end > textLen {
end = textLen
}
if start > end {
start, end = end, start
}
w.tarea.Set("selectionStart", start)
w.tarea.Set("selectionEnd", end)
}
}
// Move DOM element to position the caret.
if old.Selection.Caret != new.Selection.Caret || old.Selection.Transform != new.Selection.Transform {
pos := new.Selection.Transform.Transform(new.Selection.Caret.Pos.Add(f32.Pt(0, new.Selection.Caret.Descent)))
bounds := w.cnv.Call("getBoundingClientRect")
left := bounds.Get("left").Float() + float64(pos.X)/float64(w.scale)
top := bounds.Get("top").Float() + float64(pos.Y-new.Selection.Caret.Ascent)/float64(w.scale)
height := float64(new.Selection.Caret.Ascent+new.Selection.Caret.Descent) / float64(w.scale)
style := w.tarea.Get("style")
style.Set("left", fmt.Sprintf("%fpx", left))
style.Set("top", fmt.Sprintf("%fpx", top))
style.Set("height", fmt.Sprintf("%fpx", height))
style.Set("width", "1px")
} }
} }
func (w *window) EditorStateChanged(old, new editorState) {}
func (w *window) SetAnimating(anim bool) { func (w *window) SetAnimating(anim bool) {
w.animating = anim w.animating = anim
if anim && !w.animRequested { if anim && !w.animRequested {
@@ -863,19 +527,20 @@ func (w *window) ReadClipboard() {
if w.clipboard.IsUndefined() { if w.clipboard.IsUndefined() {
return return
} }
if w.clipboard.Get("readText").Truthy() { if w.clipboard.Get("readText").IsUndefined() {
w.clipboard.Call("readText").Call("then", w.clipboardCallback) return
} }
w.clipboard.Call("readText", w.clipboard).Call("then", w.clipboardCallback)
} }
func (w *window) WriteClipboard(mime string, s []byte) { func (w *window) WriteClipboard(s string) {
if w.clipboard.IsUndefined() { if w.clipboard.IsUndefined() {
return return
} }
if w.clipboard.Get("writeText").IsUndefined() { if w.clipboard.Get("writeText").IsUndefined() {
return return
} }
w.clipboard.Call("writeText", string(s)) w.clipboard.Call("writeText", s)
} }
func (w *window) Configure(options []Option) { func (w *window) Configure(options []Option) {
@@ -903,7 +568,7 @@ func (w *window) Configure(options []Option) {
if cnf.Decorated != prev.Decorated { if cnf.Decorated != prev.Decorated {
w.config.Decorated = cnf.Decorated w.config.Decorated = cnf.Decorated
} }
w.processEvent(ConfigEvent{Config: w.config}) w.w.Event(ConfigEvent{Config: w.config})
} }
func (w *window) Perform(system.Action) {} func (w *window) Perform(system.Action) {}
@@ -942,21 +607,23 @@ func (w *window) SetCursor(cursor pointer.Cursor) {
style.Set("cursor", webCursor[cursor]) style.Set("cursor", webCursor[cursor])
} }
func (w *window) Wakeup() {
select {
case w.wakeups <- struct{}{}:
default:
}
}
func (w *window) ShowTextInput(show bool) { func (w *window) ShowTextInput(show bool) {
// Run in a goroutine to avoid a deadlock if the // Run in a goroutine to avoid a deadlock if the
// focus change result in an event. // focus change result in an event.
if show { go func() {
w.focus() if show {
} else { w.focus()
// If we're composing, end composition first by clearing the textarea. } else {
// That is a attempt to force the browser to end composition. w.blur()
if w.composing != -1 {
w.tarea.Set("value", "")
w.composing = -1
w.w.SetComposingRegion(key.Range{Start: -1, End: -1})
} }
w.blur() }()
}
} }
func (w *window) SetInputHint(mode key.InputHint) { func (w *window) SetInputHint(mode key.InputHint) {
@@ -973,7 +640,7 @@ func (w *window) resize() {
} }
if size != w.config.Size { if size != w.config.Size {
w.config.Size = size w.config.Size = size
w.processEvent(ConfigEvent{Config: w.config}) w.w.Event(ConfigEvent{Config: w.config})
} }
if vx, vy := w.visualViewport.Get("width"), w.visualViewport.Get("height"); !vx.IsUndefined() && !vy.IsUndefined() { if vx, vy := w.visualViewport.Get("width"), w.visualViewport.Get("height"); !vx.IsUndefined() && !vy.IsUndefined() {
@@ -993,20 +660,13 @@ func (w *window) draw(sync bool) {
if w.contextStatus == contextStatusLost { if w.contextStatus == contextStatusLost {
return return
} }
anim := w.animating
w.animRequested = anim
if anim {
w.requestAnimationFrame.Invoke(w.redraw)
} else if !sync {
return
}
size, insets, metric := w.getConfig() size, insets, metric := w.getConfig()
if metric == (unit.Metric{}) || size.X == 0 || size.Y == 0 { if metric == (unit.Metric{}) || size.X == 0 || size.Y == 0 {
return return
} }
w.processEvent(frameEvent{ w.w.Event(frameEvent{
FrameEvent: FrameEvent{ FrameEvent: system.FrameEvent{
Now: time.Now(), Now: time.Now(),
Size: size, Size: size,
Insets: insets, Insets: insets,
@@ -1016,10 +676,10 @@ func (w *window) draw(sync bool) {
}) })
} }
func (w *window) getConfig() (image.Point, Insets, unit.Metric) { func (w *window) getConfig() (image.Point, system.Insets, unit.Metric) {
invscale := unit.Dp(1. / w.scale) invscale := unit.Dp(1. / w.scale)
return image.Pt(w.config.Size.X, w.config.Size.Y), return image.Pt(w.config.Size.X, w.config.Size.Y),
Insets{ system.Insets{
Bottom: unit.Dp(w.inset.Y) * invscale, Bottom: unit.Dp(w.inset.Y) * invscale,
Right: unit.Dp(w.inset.X) * invscale, Right: unit.Dp(w.inset.X) * invscale,
}, unit.Metric{ }, unit.Metric{
@@ -1075,12 +735,19 @@ func (w *window) navigationColor(c color.NRGBA) {
theme.Set("content", fmt.Sprintf("#%06X", []uint8{rgba.R, rgba.G, rgba.B})) theme.Set("content", fmt.Sprintf("#%06X", []uint8{rgba.R, rgba.G, rgba.B}))
} }
func (w *window) requestRedraw() {
select {
case w.chanRedraw <- struct{}{}:
default:
}
}
func osMain() { func osMain() {
select {} select {}
} }
func translateKey(k string) (key.Name, bool) { func translateKey(k string) (string, bool) {
var n key.Name var n string
switch k { switch k {
case "ArrowUp": case "ArrowUp":
@@ -1147,15 +814,11 @@ func translateKey(k string) (key.Name, bool) {
r, s := utf8.DecodeRuneInString(k) r, s := utf8.DecodeRuneInString(k)
// If there is exactly one printable character, return that. // If there is exactly one printable character, return that.
if s == len(k) && unicode.IsPrint(r) { if s == len(k) && unicode.IsPrint(r) {
return key.Name(strings.ToUpper(k)), true return strings.ToUpper(k), true
} }
return "", false return "", false
} }
return n, true return n, true
} }
func (JSViewEvent) implementsViewEvent() {} func (_ ViewEvent) ImplementsEvent() {}
func (JSViewEvent) ImplementsEvent() {}
func (j JSViewEvent) Valid() bool {
return !(j.Element.IsNull() || j.Element.IsUndefined())
}
+322 -514
View File
File diff suppressed because it is too large Load Diff
+66 -124
View File
@@ -6,7 +6,7 @@
#include "_cgo_export.h" #include "_cgo_export.h"
__attribute__ ((visibility ("hidden"))) CALayer *gio_layerFactory(BOOL presentWithTrans); __attribute__ ((visibility ("hidden"))) CALayer *gio_layerFactory(void);
@interface GioAppDelegate : NSObject<NSApplicationDelegate> @interface GioAppDelegate : NSObject<NSApplicationDelegate>
@end @end
@@ -14,55 +14,40 @@ __attribute__ ((visibility ("hidden"))) CALayer *gio_layerFactory(BOOL presentWi
@interface GioWindowDelegate : NSObject<NSWindowDelegate> @interface GioWindowDelegate : NSObject<NSWindowDelegate>
@end @end
@interface GioView : NSView <CALayerDelegate,NSTextInputClient>
@property uintptr_t handle;
@property BOOL presentWithTrans;
@end
@implementation GioWindowDelegate @implementation GioWindowDelegate
- (void)windowWillMiniaturize:(NSNotification *)notification { - (void)windowWillMiniaturize:(NSNotification *)notification {
NSWindow *window = (NSWindow *)[notification object]; NSWindow *window = (NSWindow *)[notification object];
GioView *view = (GioView *)window.contentView; gio_onHide((__bridge CFTypeRef)window.contentView);
gio_onDraw(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; gio_onShow((__bridge CFTypeRef)window.contentView);
gio_onDraw(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; gio_onFullscreen((__bridge CFTypeRef)window.contentView);
gio_onDraw(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; gio_onWindowed((__bridge CFTypeRef)window.contentView);
gio_onDraw(view.handle);
} }
- (void)windowDidChangeScreen:(NSNotification *)notification { - (void)windowDidChangeScreen:(NSNotification *)notification {
NSWindow *window = (NSWindow *)[notification object]; NSWindow *window = (NSWindow *)[notification object];
CGDirectDisplayID dispID = [[[window screen] deviceDescription][@"NSScreenNumber"] unsignedIntValue]; CGDirectDisplayID dispID = [[[window screen] deviceDescription][@"NSScreenNumber"] unsignedIntValue];
GioView *view = (GioView *)window.contentView; CFTypeRef view = (__bridge CFTypeRef)window.contentView;
gio_onChangeScreen(view.handle, dispID); gio_onChangeScreen(view, dispID);
} }
- (void)windowDidBecomeKey:(NSNotification *)notification { - (void)windowDidBecomeKey:(NSNotification *)notification {
NSWindow *window = (NSWindow *)[notification object]; NSWindow *window = (NSWindow *)[notification object];
GioView *view = (GioView *)window.contentView; gio_onFocus((__bridge CFTypeRef)window.contentView, 1);
if ([window firstResponder] == view) {
gio_onFocus(view.handle, 1);
}
} }
- (void)windowDidResignKey:(NSNotification *)notification { - (void)windowDidResignKey:(NSNotification *)notification {
NSWindow *window = (NSWindow *)[notification object]; NSWindow *window = (NSWindow *)[notification object];
GioView *view = (GioView *)window.contentView; gio_onFocus((__bridge CFTypeRef)window.contentView, 0);
if ([window firstResponder] == view) {
gio_onFocus(view.handle, 0);
}
} }
@end @end
static void handleMouse(GioView *view, NSEvent *event, int typ, CGFloat dx, CGFloat dy) { static void handleMouse(NSView *view, NSEvent *event, int typ, CGFloat dx, CGFloat dy) {
NSPoint p = [view convertPoint:[event locationInWindow] fromView:nil]; NSPoint p = [view convertPoint:[event locationInWindow] fromView:nil];
if (!event.hasPreciseScrollingDeltas) { if (!event.hasPreciseScrollingDeltas) {
// dx and dy are in rows and columns. // dx and dy are in rows and columns.
@@ -71,9 +56,12 @@ static void handleMouse(GioView *view, NSEvent *event, int typ, CGFloat dx, CGFl
} }
// Origin is in the lower left corner. Convert to upper left. // Origin is in the lower left corner. Convert to upper left.
CGFloat height = view.bounds.size.height; CGFloat height = view.bounds.size.height;
gio_onMouse(view.handle, (__bridge CFTypeRef)event, typ, event.buttonNumber, p.x, height - p.y, dx, dy, [event timestamp], [event modifierFlags]); gio_onMouse((__bridge CFTypeRef)view, (__bridge CFTypeRef)event, typ, event.buttonNumber, p.x, height - p.y, dx, dy, [event timestamp], [event modifierFlags]);
} }
@interface GioView : NSView <CALayerDelegate,NSTextInputClient>
@end
@implementation GioView @implementation GioView
- (void)setFrameSize:(NSSize)newSize { - (void)setFrameSize:(NSSize)newSize {
[super setFrameSize:newSize]; [super setFrameSize:newSize];
@@ -82,19 +70,21 @@ static void handleMouse(GioView *view, NSEvent *event, int typ, CGFloat dx, CGFl
// drawRect is called when OpenGL is used, displayLayer otherwise. // drawRect is called when OpenGL is used, displayLayer otherwise.
// Don't know why. // Don't know why.
- (void)drawRect:(NSRect)r { - (void)drawRect:(NSRect)r {
gio_onDraw(self.handle); gio_onDraw((__bridge CFTypeRef)self);
} }
- (void)displayLayer:(CALayer *)layer { - (void)displayLayer:(CALayer *)layer {
layer.contentsScale = self.window.backingScaleFactor; layer.contentsScale = self.window.backingScaleFactor;
gio_onDraw(self.handle); gio_onDraw((__bridge CFTypeRef)self);
} }
- (CALayer *)makeBackingLayer { - (CALayer *)makeBackingLayer {
CALayer *layer = gio_layerFactory(self.presentWithTrans); CALayer *layer = gio_layerFactory();
layer.delegate = self; layer.delegate = self;
return layer; return layer;
} }
- (void)viewDidMoveToWindow { - (void)viewDidMoveToWindow {
gio_onAttached(self.handle, self.window != nil ? 1 : 0); if (self.window == nil) {
gio_onClose((__bridge CFTypeRef)self);
}
} }
- (void)mouseDown:(NSEvent *)event { - (void)mouseDown:(NSEvent *)event {
handleMouse(self, event, MOUSE_DOWN, 0, 0); handleMouse(self, event, MOUSE_DOWN, 0, 0);
@@ -132,37 +122,34 @@ 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((__bridge CFTypeRef)self, (__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((__bridge CFTypeRef)self, (__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((__bridge CFTypeRef)self, (__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((__bridge CFTypeRef)self);
return res ? YES : NO; return res ? YES : NO;
} }
- (NSRange)markedRange { - (NSRange)markedRange {
return gio_markedRange(self.handle); return gio_markedRange((__bridge CFTypeRef)self);
} }
- (NSRange)selectedRange { - (NSRange)selectedRange {
return gio_selectedRange(self.handle); return gio_selectedRange((__bridge CFTypeRef)self);
} }
- (void)unmarkText { - (void)unmarkText {
gio_unmarkText(self.handle); gio_unmarkText((__bridge CFTypeRef)self);
} }
- (void)setMarkedText:(id)string - (void)setMarkedText:(id)string
selectedRange:(NSRange)selRange selectedRange:(NSRange)selRange
@@ -174,14 +161,14 @@ static void handleMouse(GioView *view, NSEvent *event, int typ, CGFloat dx, CGFl
} else { } else {
str = string; str = string;
} }
gio_setMarkedText(self.handle, (__bridge CFTypeRef)str, selRange, replaceRange); gio_setMarkedText((__bridge CFTypeRef)self, (__bridge CFTypeRef)str, selRange, replaceRange);
} }
- (NSArray<NSAttributedStringKey> *)validAttributesForMarkedText { - (NSArray<NSAttributedStringKey> *)validAttributesForMarkedText {
return nil; return nil;
} }
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range - (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range
actualRange:(NSRangePointer)actualRange { actualRange:(NSRangePointer)actualRange {
NSString *str = CFBridgingRelease(gio_substringForProposedRange(self.handle, range, actualRange)); NSString *str = CFBridgingRelease(gio_substringForProposedRange((__bridge CFTypeRef)self, range, actualRange));
return [[NSAttributedString alloc] initWithString:str attributes:nil]; return [[NSAttributedString alloc] initWithString:str attributes:nil];
} }
- (void)insertText:(id)string - (void)insertText:(id)string
@@ -193,34 +180,17 @@ static void handleMouse(GioView *view, NSEvent *event, int typ, CGFloat dx, CGFl
} else { } else {
str = string; str = string;
} }
gio_insertText(self.handle, (__bridge CFTypeRef)str, replaceRange); gio_insertText((__bridge CFTypeRef)self, (__bridge CFTypeRef)str, replaceRange);
} }
- (NSUInteger)characterIndexForPoint:(NSPoint)p { - (NSUInteger)characterIndexForPoint:(NSPoint)p {
return gio_characterIndexForPoint(self.handle, p); return gio_characterIndexForPoint((__bridge CFTypeRef)self, p);
} }
- (NSRect)firstRectForCharacterRange:(NSRange)rng - (NSRect)firstRectForCharacterRange:(NSRange)rng
actualRange:(NSRangePointer)actual { actualRange:(NSRangePointer)actual {
NSRect r = gio_firstRectForCharacterRange(self.handle, rng, actual); NSRect r = gio_firstRectForCharacterRange((__bridge CFTypeRef)self, rng, actual);
r = [self convertRect:r toView:nil]; r = [self convertRect:r toView:nil];
return [[self window] convertRectToScreen:r]; return [[self window] convertRectToScreen:r];
} }
- (void)applicationWillUnhide:(NSNotification *)notification {
gio_onDraw(self.handle);
}
- (void)applicationDidHide:(NSNotification *)notification {
gio_onDraw(self.handle);
}
- (void)dealloc {
gio_onDestroy(self.handle);
}
- (BOOL) becomeFirstResponder {
gio_onFocus(self.handle, 1);
return [super becomeFirstResponder];
}
- (BOOL) resignFirstResponder {
gio_onFocus(self.handle, 0);
return [super resignFirstResponder];
}
@end @end
// Delegates are weakly referenced from their peers. Nothing // Delegates are weakly referenced from their peers. Nothing
@@ -270,7 +240,7 @@ void gio_showCursor() {
// some cursors are not public, this tries to use a private cursor // some cursors are not public, this tries to use a private cursor
// and uses fallback when the use of private cursor fails. // and uses fallback when the use of private cursor fails.
static void trySetPrivateCursor(SEL cursorName, NSCursor* fallback) { void gio_trySetPrivateCursor(SEL cursorName, NSCursor* fallback) {
if ([NSCursor respondsToSelector:cursorName]) { if ([NSCursor respondsToSelector:cursorName]) {
id object = [NSCursor performSelector:cursorName]; id object = [NSCursor performSelector:cursorName];
if ([object isKindOfClass:[NSCursor class]]) { if ([object isKindOfClass:[NSCursor class]]) {
@@ -302,7 +272,7 @@ void gio_setCursor(NSUInteger curID) {
break; break;
case 6: // pointer.CursorAllScroll case 6: // pointer.CursorAllScroll
// For some reason, using _moveCursor fails on Monterey. // For some reason, using _moveCursor fails on Monterey.
// trySetPrivateCursor(@selector(_moveCursor), NSCursor.arrowCursor); // gio_trySetPrivateCursor(@selector(_moveCursor), NSCursor.arrowCursor);
[NSCursor.arrowCursor set]; [NSCursor.arrowCursor set];
break; break;
case 7: // pointer.CursorColResize case 7: // pointer.CursorColResize
@@ -312,31 +282,33 @@ void gio_setCursor(NSUInteger curID) {
[NSCursor.resizeUpDownCursor set]; [NSCursor.resizeUpDownCursor set];
break; break;
case 9: // pointer.CursorGrab case 9: // pointer.CursorGrab
[NSCursor.openHandCursor set]; // [NSCursor.openHandCursor set];
gio_trySetPrivateCursor(@selector(openHandCursor), NSCursor.arrowCursor);
break; break;
case 10: // pointer.CursorGrabbing case 10: // pointer.CursorGrabbing
[NSCursor.closedHandCursor set]; // [NSCursor.closedHandCursor set];
gio_trySetPrivateCursor(@selector(closedHandCursor), NSCursor.arrowCursor);
break; break;
case 11: // pointer.CursorNotAllowed case 11: // pointer.CursorNotAllowed
[NSCursor.operationNotAllowedCursor set]; [NSCursor.operationNotAllowedCursor set];
break; break;
case 12: // pointer.CursorWait case 12: // pointer.CursorWait
trySetPrivateCursor(@selector(busyButClickableCursor), NSCursor.arrowCursor); gio_trySetPrivateCursor(@selector(busyButClickableCursor), NSCursor.arrowCursor);
break; break;
case 13: // pointer.CursorProgress case 13: // pointer.CursorProgress
trySetPrivateCursor(@selector(busyButClickableCursor), NSCursor.arrowCursor); gio_trySetPrivateCursor(@selector(busyButClickableCursor), NSCursor.arrowCursor);
break; break;
case 14: // pointer.CursorNorthWestResize case 14: // pointer.CursorNorthWestResize
trySetPrivateCursor(@selector(_windowResizeNorthWestCursor), NSCursor.resizeUpDownCursor); gio_trySetPrivateCursor(@selector(_windowResizeNorthWestCursor), NSCursor.resizeUpDownCursor);
break; break;
case 15: // pointer.CursorNorthEastResize case 15: // pointer.CursorNorthEastResize
trySetPrivateCursor(@selector(_windowResizeNorthEastCursor), NSCursor.resizeUpDownCursor); gio_trySetPrivateCursor(@selector(_windowResizeNorthEastCursor), NSCursor.resizeUpDownCursor);
break; break;
case 16: // pointer.CursorSouthWestResize case 16: // pointer.CursorSouthWestResize
trySetPrivateCursor(@selector(_windowResizeSouthWestCursor), NSCursor.resizeUpDownCursor); gio_trySetPrivateCursor(@selector(_windowResizeSouthWestCursor), NSCursor.resizeUpDownCursor);
break; break;
case 17: // pointer.CursorSouthEastResize case 17: // pointer.CursorSouthEastResize
trySetPrivateCursor(@selector(_windowResizeSouthEastCursor), NSCursor.resizeUpDownCursor); gio_trySetPrivateCursor(@selector(_windowResizeSouthEastCursor), NSCursor.resizeUpDownCursor);
break; break;
case 18: // pointer.CursorNorthSouthResize case 18: // pointer.CursorNorthSouthResize
[NSCursor.resizeUpDownCursor set]; [NSCursor.resizeUpDownCursor set];
@@ -357,10 +329,10 @@ void gio_setCursor(NSUInteger curID) {
[NSCursor.resizeDownCursor set]; [NSCursor.resizeDownCursor set];
break; break;
case 24: // pointer.CursorNorthEastSouthWestResize case 24: // pointer.CursorNorthEastSouthWestResize
trySetPrivateCursor(@selector(_windowResizeNorthEastSouthWestCursor), NSCursor.resizeUpDownCursor); gio_trySetPrivateCursor(@selector(_windowResizeNorthEastSouthWestCursor), NSCursor.resizeUpDownCursor);
break; break;
case 25: // pointer.CursorNorthWestSouthEastResize case 25: // pointer.CursorNorthWestSouthEastResize
trySetPrivateCursor(@selector(_windowResizeNorthWestSouthEastCursor), NSCursor.resizeUpDownCursor); gio_trySetPrivateCursor(@selector(_windowResizeNorthWestSouthEastCursor), NSCursor.resizeUpDownCursor);
break; break;
default: default:
[NSCursor.arrowCursor set]; [NSCursor.arrowCursor set];
@@ -369,7 +341,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,50 +353,42 @@ 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;
} }
} }
CFTypeRef gio_createView(int presentWithTrans) { CFTypeRef gio_createView(void) {
@autoreleasepool { @autoreleasepool {
NSRect frame = NSMakeRect(0, 0, 0, 0); NSRect frame = NSMakeRect(0, 0, 0, 0);
GioView* view = [[GioView alloc] initWithFrame:frame]; GioView* view = [[GioView alloc] initWithFrame:frame];
view.presentWithTrans = presentWithTrans ? YES : NO;
view.wantsLayer = YES; view.wantsLayer = YES;
view.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize; view.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;
[[NSNotificationCenter defaultCenter] addObserver:view
selector:@selector(applicationWillUnhide:)
name:NSApplicationWillUnhideNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:view
selector:@selector(applicationDidHide:)
name:NSApplicationDidHideNotification
object:nil];
return CFBridgingRetain(view); return CFBridgingRetain(view);
} }
} }
void gio_viewSetHandle(CFTypeRef viewRef, uintptr_t handle) {
@autoreleasepool {
GioView *v = (__bridge GioView *)viewRef;
v.handle = handle;
}
}
@implementation GioAppDelegate @implementation GioAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
[NSApp activateIgnoringOtherApps:YES]; [NSApp activateIgnoringOtherApps:YES];
gio_onFinishLaunching();
} }
- (void)application:(NSApplication *)application openURLs:(NSArray<NSURL *> *)urls { - (void)applicationDidHide:(NSNotification *)aNotification {
for (NSURL *url in urls) { gio_onAppHide();
gio_onOpenURI((__bridge CFTypeRef)url.absoluteString); }
} - (void)applicationWillUnhide:(NSNotification *)notification {
gio_onAppShow();
} }
@end @end
@@ -455,25 +419,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];
}
}
+13 -11
View File
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: Unlicense OR MIT // SPDX-License-Identifier: Unlicense OR MIT
//go:build (linux && !android) || freebsd || openbsd //go:build (linux && !android) || freebsd || openbsd
// +build linux,!android freebsd openbsd
package app package app
@@ -11,6 +12,13 @@ import (
"gioui.org/io/pointer" "gioui.org/io/pointer"
) )
// ViewEvent provides handles to the underlying window objects for the
// current display protocol.
type ViewEvent interface {
implementsViewEvent()
ImplementsEvent()
}
type X11ViewEvent struct { type X11ViewEvent struct {
// Display is a pointer to the X11 Display created by XOpenDisplay. // Display is a pointer to the X11 Display created by XOpenDisplay.
Display unsafe.Pointer Display unsafe.Pointer
@@ -20,9 +28,6 @@ type X11ViewEvent struct {
func (X11ViewEvent) implementsViewEvent() {} func (X11ViewEvent) implementsViewEvent() {}
func (X11ViewEvent) ImplementsEvent() {} func (X11ViewEvent) ImplementsEvent() {}
func (x X11ViewEvent) Valid() bool {
return x != (X11ViewEvent{})
}
type WaylandViewEvent struct { type WaylandViewEvent struct {
// Display is the *wl_display returned by wl_display_connect. // Display is the *wl_display returned by wl_display_connect.
@@ -33,9 +38,6 @@ type WaylandViewEvent struct {
func (WaylandViewEvent) implementsViewEvent() {} func (WaylandViewEvent) implementsViewEvent() {}
func (WaylandViewEvent) ImplementsEvent() {} func (WaylandViewEvent) ImplementsEvent() {}
func (w WaylandViewEvent) Valid() bool {
return w != (WaylandViewEvent{})
}
func osMain() { func osMain() {
select {} select {}
@@ -47,7 +49,7 @@ type windowDriver func(*callbacks, []Option) error
// let each driver initialize these variables with their own version of createWindow. // let each driver initialize these variables with their own version of createWindow.
var wlDriver, x11Driver windowDriver var wlDriver, x11Driver windowDriver
func newWindow(window *callbacks, options []Option) { func newWindow(window *callbacks, options []Option) error {
var errFirst error var errFirst error
for _, d := range []windowDriver{wlDriver, x11Driver} { for _, d := range []windowDriver{wlDriver, x11Driver} {
if d == nil { if d == nil {
@@ -55,16 +57,16 @@ func newWindow(window *callbacks, options []Option) {
} }
err := d(window, options) err := d(window, options)
if err == nil { if err == nil {
return return nil
} }
if errFirst == nil { if errFirst == nil {
errFirst = err errFirst = err
} }
} }
if errFirst == nil { if errFirst != nil {
errFirst = errors.New("app: no window driver available") return errFirst
} }
window.ProcessEvent(DestroyEvent{Err: errFirst}) return errors.New("app: no window driver available")
} }
// xCursor contains mapping from pointer.Cursor to XCursor. // xCursor contains mapping from pointer.Cursor to XCursor.
+131 -202
View File
@@ -15,7 +15,6 @@ import (
"math" "math"
"os" "os"
"os/exec" "os/exec"
"runtime"
"strconv" "strconv"
"sync" "sync"
"time" "time"
@@ -26,12 +25,10 @@ import (
"gioui.org/app/internal/xkb" "gioui.org/app/internal/xkb"
"gioui.org/f32" "gioui.org/f32"
"gioui.org/internal/fling" "gioui.org/internal/fling"
"gioui.org/io/event" "gioui.org/io/clipboard"
"gioui.org/io/key" "gioui.org/io/key"
"gioui.org/io/pointer" "gioui.org/io/pointer"
"gioui.org/io/system" "gioui.org/io/system"
"gioui.org/io/transfer"
"gioui.org/op"
"gioui.org/unit" "gioui.org/unit"
) )
@@ -100,9 +97,7 @@ type wlDisplay struct {
read, write int read, write int
} }
repeat repeatState repeat repeatState
poller poller
readClipClose chan struct{}
} }
type wlSeat struct { type wlSeat struct {
@@ -116,8 +111,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
@@ -144,7 +137,7 @@ type repeatState struct {
delay time.Duration delay time.Duration
key uint32 key uint32
win *window win *callbacks
stopC chan struct{} stopC chan struct{}
start time.Duration start time.Duration
@@ -156,6 +149,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
@@ -200,10 +194,12 @@ type window struct {
dir f32.Point dir f32.Point
} }
configured bool stage system.Stage
dead bool
lastFrameCallback *C.struct_wl_callback lastFrameCallback *C.struct_wl_callback
animating bool animating bool
redraw bool
// The most recent configure serial waiting to be ack'ed. // The most recent configure serial waiting to be ack'ed.
serial C.uint32_t serial C.uint32_t
scale int scale int
@@ -213,11 +209,9 @@ type window struct {
wsize image.Point // window config size before going fullscreen or maximized wsize image.Point // window config size before going fullscreen or maximized
inCompositor bool // window is moving or being resized inCompositor bool // window is moving or being resized
clipReads chan transfer.DataEvent clipReads chan clipboard.Event
wakeups chan struct{} wakeups chan struct{}
closing bool
} }
type poller struct { type poller struct {
@@ -266,17 +260,25 @@ func newWLWindow(callbacks *callbacks, options []Option) error {
return err return err
} }
w.w = callbacks w.w = callbacks
w.w.SetDriver(w) go func() {
defer d.destroy()
defer w.destroy()
// Finish and commit setup from createNativeWindow. w.w.SetDriver(w)
w.Configure(options)
w.draw(true)
C.wl_surface_commit(w.surf)
w.ProcessEvent(WaylandViewEvent{ // Finish and commit setup from createNativeWindow.
Display: unsafe.Pointer(w.display()), w.Configure(options)
Surface: unsafe.Pointer(w.surf), C.wl_surface_commit(w.surf)
})
w.w.Event(WaylandViewEvent{
Display: unsafe.Pointer(w.display()),
Surface: unsafe.Pointer(w.surf),
})
err := w.loop()
w.w.Event(WaylandViewEvent{})
w.w.Event(system.DestroyEvent{Err: err})
}()
return nil return nil
} }
@@ -352,7 +354,7 @@ func (d *wlDisplay) createNativeWindow(options []Option) (*window, error) {
ppdp: ppdp, ppdp: ppdp,
ppsp: ppdp, ppsp: ppdp,
wakeups: make(chan struct{}, 1), wakeups: make(chan struct{}, 1),
clipReads: make(chan transfer.DataEvent, 1), clipReads: make(chan clipboard.Event, 1),
} }
w.surf = C.wl_compositor_create_surface(d.compositor) w.surf = C.wl_compositor_create_surface(d.compositor)
if w.surf == nil { if w.surf == nil {
@@ -547,15 +549,15 @@ func gio_onSeatName(data unsafe.Pointer, seat *C.struct_wl_seat, name *C.char) {
func gio_onXdgSurfaceConfigure(data unsafe.Pointer, wmSurf *C.struct_xdg_surface, serial C.uint32_t) { func gio_onXdgSurfaceConfigure(data unsafe.Pointer, wmSurf *C.struct_xdg_surface, serial C.uint32_t) {
w := callbackLoad(data).(*window) w := callbackLoad(data).(*window)
w.serial = serial w.serial = serial
w.redraw = true
C.xdg_surface_ack_configure(wmSurf, serial) C.xdg_surface_ack_configure(wmSurf, serial)
w.configured = true w.setStage(system.StageRunning)
w.draw(true)
} }
//export gio_onToplevelClose //export gio_onToplevelClose
func gio_onToplevelClose(data unsafe.Pointer, topLvl *C.struct_xdg_toplevel) { func gio_onToplevelClose(data unsafe.Pointer, topLvl *C.struct_xdg_toplevel) {
w := callbackLoad(data).(*window) w := callbackLoad(data).(*window)
w.closing = true w.dead = true
} }
//export gio_onToplevelConfigure //export gio_onToplevelConfigure
@@ -584,8 +586,8 @@ func gio_onToplevelDecorationConfigure(data unsafe.Pointer, deco *C.struct_zxdg_
} else { } else {
w.size.Y += int(w.config.decoHeight) w.size.Y += int(w.config.decoHeight)
} }
w.ProcessEvent(ConfigEvent{Config: w.config}) w.w.Event(ConfigEvent{Config: w.config})
w.draw(true) w.redraw = true
} }
} }
@@ -643,7 +645,7 @@ func gio_onSurfaceEnter(data unsafe.Pointer, surf *C.struct_wl_surface, output *
if w.config.Mode == Minimized { if w.config.Mode == Minimized {
// Minimized window got brought back up: it is no longer so. // Minimized window got brought back up: it is no longer so.
w.config.Mode = Windowed w.config.Mode = Windowed
w.ProcessEvent(ConfigEvent{Config: w.config}) w.w.Event(ConfigEvent{Config: w.config})
} }
} }
@@ -788,7 +790,7 @@ func gio_onTouchDown(data unsafe.Pointer, touch *C.struct_wl_touch, serial, t C.
X: fromFixed(x) * float32(w.scale), X: fromFixed(x) * float32(w.scale),
Y: fromFixed(y) * float32(w.scale), Y: fromFixed(y) * float32(w.scale),
} }
w.ProcessEvent(pointer.Event{ w.w.Event(pointer.Event{
Kind: pointer.Press, Kind: pointer.Press,
Source: pointer.Touch, Source: pointer.Touch,
Position: w.lastTouch, Position: w.lastTouch,
@@ -804,7 +806,7 @@ func gio_onTouchUp(data unsafe.Pointer, touch *C.struct_wl_touch, serial, t C.ui
s.serial = serial s.serial = serial
w := s.touchFoci[id] w := s.touchFoci[id]
delete(s.touchFoci, id) delete(s.touchFoci, id)
w.ProcessEvent(pointer.Event{ w.w.Event(pointer.Event{
Kind: pointer.Release, Kind: pointer.Release,
Source: pointer.Touch, Source: pointer.Touch,
Position: w.lastTouch, Position: w.lastTouch,
@@ -822,7 +824,7 @@ func gio_onTouchMotion(data unsafe.Pointer, touch *C.struct_wl_touch, t C.uint32
X: fromFixed(x) * float32(w.scale), X: fromFixed(x) * float32(w.scale),
Y: fromFixed(y) * float32(w.scale), Y: fromFixed(y) * float32(w.scale),
} }
w.ProcessEvent(pointer.Event{ w.w.Event(pointer.Event{
Kind: pointer.Move, Kind: pointer.Move,
Position: w.lastTouch, Position: w.lastTouch,
Source: pointer.Touch, Source: pointer.Touch,
@@ -841,7 +843,7 @@ func gio_onTouchCancel(data unsafe.Pointer, touch *C.struct_wl_touch) {
s := callbackLoad(data).(*wlSeat) s := callbackLoad(data).(*wlSeat)
for id, w := range s.touchFoci { for id, w := range s.touchFoci {
delete(s.touchFoci, id) delete(s.touchFoci, id)
w.ProcessEvent(pointer.Event{ w.w.Event(pointer.Event{
Kind: pointer.Cancel, Kind: pointer.Cancel,
Source: pointer.Touch, Source: pointer.Touch,
}) })
@@ -852,8 +854,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,12 +864,12 @@ 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.w.Event(pointer.Event{Kind: pointer.Cancel})
} }
} }
@@ -884,13 +886,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 +900,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
} }
@@ -934,7 +930,7 @@ func gio_onPointerButton(data unsafe.Pointer, p *C.struct_wl_pointer, serial, t,
} }
w.flushScroll() w.flushScroll()
w.resetFling() w.resetFling()
w.ProcessEvent(pointer.Event{ w.w.Event(pointer.Event{
Kind: kind, Kind: kind,
Source: pointer.Mouse, Source: pointer.Mouse,
Buttons: w.pointerBtns, Buttons: w.pointerBtns,
@@ -970,9 +966,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()
} }
@@ -1025,34 +1018,23 @@ func gio_onPointerAxisDiscrete(data unsafe.Pointer, p *C.struct_wl_pointer, axis
} }
func (w *window) ReadClipboard() { func (w *window) ReadClipboard() {
if w.disp.readClipClose != nil {
return
}
w.disp.readClipClose = make(chan struct{})
r, err := w.disp.readClipboard() r, err := w.disp.readClipboard()
// Send empty responses on unavailable clipboards or errors.
if r == nil || err != nil { if r == nil || err != nil {
w.w.Event(clipboard.Event{})
return return
} }
// Don't let slow clipboard transfers block event loop. // Don't let slow clipboard transfers block event loop.
go func() { go func() {
defer r.Close() defer r.Close()
data, _ := io.ReadAll(r) data, _ := io.ReadAll(r)
e := transfer.DataEvent{ w.clipReads <- clipboard.Event{Text: string(data)}
Type: "application/text", w.Wakeup()
Open: func() io.ReadCloser {
return io.NopCloser(bytes.NewReader(data))
},
}
select {
case w.clipReads <- e:
w.disp.wakeup()
case <-w.disp.readClipClose:
}
}() }()
} }
func (w *window) WriteClipboard(mime string, s []byte) { func (w *window) WriteClipboard(s string) {
w.disp.writeClipboard(s) w.disp.writeClipboard([]byte(s))
} }
func (w *window) Configure(options []Option) { func (w *window) Configure(options []Option) {
@@ -1110,7 +1092,8 @@ func (w *window) Configure(options []Option) {
w.config.MaxSize = cnf.MaxSize w.config.MaxSize = cnf.MaxSize
w.setWindowConstraints() w.setWindowConstraints()
} }
w.ProcessEvent(ConfigEvent{Config: w.config}) w.w.Event(ConfigEvent{Config: w.config})
w.redraw = true
} }
func (w *window) setWindowConstraints() { func (w *window) setWindowConstraints() {
@@ -1147,23 +1130,22 @@ func (w *window) Perform(actions system.Action) {
walkActions(actions, func(action system.Action) { walkActions(actions, func(action system.Action) {
switch action { switch action {
case system.ActionClose: case system.ActionClose:
w.closing = true w.dead = true
} }
}) })
} }
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 +1158,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 +1171,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].
@@ -1232,8 +1213,7 @@ func gio_onKeyboardEnter(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, se
w := callbackLoad(unsafe.Pointer(surf)).(*window) w := callbackLoad(unsafe.Pointer(surf)).(*window)
s.keyboardFocus = w s.keyboardFocus = w
s.disp.repeat.Stop(0) s.disp.repeat.Stop(0)
w.config.Focused = true w.w.Event(key.FocusEvent{Focus: true})
w.ProcessEvent(ConfigEvent{Config: w.config})
} }
//export gio_onKeyboardLeave //export gio_onKeyboardLeave
@@ -1242,8 +1222,7 @@ func gio_onKeyboardLeave(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, se
s.serial = serial s.serial = serial
s.disp.repeat.Stop(0) s.disp.repeat.Stop(0)
w := s.keyboardFocus w := s.keyboardFocus
w.config.Focused = false w.w.Event(key.FocusEvent{Focus: false})
w.ProcessEvent(ConfigEvent{Config: w.config})
} }
//export gio_onKeyboardKey //export gio_onKeyboardKey
@@ -1261,7 +1240,7 @@ func gio_onKeyboardKey(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, seri
// There's no support for IME yet. // There's no support for IME yet.
w.w.EditorInsert(ee.Text) w.w.EditorInsert(ee.Text)
} else { } else {
w.ProcessEvent(e) w.w.Event(e)
} }
} }
if state != C.WL_KEYBOARD_KEY_STATE_PRESSED { if state != C.WL_KEYBOARD_KEY_STATE_PRESSED {
@@ -1296,7 +1275,7 @@ func (r *repeatState) Start(w *window, keyCode uint32, t time.Duration) {
r.now = 0 r.now = 0
r.stopC = stopC r.stopC = stopC
r.key = keyCode r.key = keyCode
r.win = w r.win = w.w
rate, delay := r.rate, r.delay rate, delay := r.rate, r.delay
go func() { go func() {
timer := time.NewTimer(delay) timer := time.NewTimer(delay)
@@ -1354,9 +1333,9 @@ func (r *repeatState) Repeat(d *wlDisplay) {
for _, e := range d.xkb.DispatchKey(r.key, key.Press) { for _, e := range d.xkb.DispatchKey(r.key, key.Press) {
if ee, ok := e.(key.EditEvent); ok { if ee, ok := e.(key.EditEvent); ok {
// There's no support for IME yet. // There's no support for IME yet.
r.win.w.EditorInsert(ee.Text) r.win.EditorInsert(ee.Text)
} else { } else {
r.win.ProcessEvent(e) r.win.Event(e)
} }
} }
r.last += delay r.last += delay
@@ -1369,68 +1348,28 @@ func gio_onFrameDone(data unsafe.Pointer, callback *C.struct_wl_callback, t C.ui
w := callbackLoad(data).(*window) w := callbackLoad(data).(*window)
if w.lastFrameCallback == callback { if w.lastFrameCallback == callback {
w.lastFrameCallback = nil w.lastFrameCallback = nil
w.draw(false)
} }
} }
func (w *window) close(err error) { func (w *window) loop() error {
w.ProcessEvent(WaylandViewEvent{}) var p poller
w.ProcessEvent(DestroyEvent{Err: err})
w.destroy()
w.disp.destroy()
w.disp = nil
}
func (w *window) dispatch() {
if w.disp == nil {
<-w.wakeups
w.w.Invalidate()
return
}
if err := w.disp.dispatch(); err != nil || w.closing {
w.close(err)
return
}
select {
case e := <-w.clipReads:
w.disp.readClipClose = nil
w.ProcessEvent(e)
case <-w.wakeups:
w.w.Invalidate()
default:
}
}
func (w *window) ProcessEvent(e event.Event) {
w.w.ProcessEvent(e)
}
func (w *window) Event() event.Event {
for { for {
evt, ok := w.w.nextEvent() if err := w.disp.dispatch(&p); err != nil {
if !ok { return err
w.dispatch()
continue
} }
return evt select {
case e := <-w.clipReads:
w.w.Event(e)
case <-w.wakeups:
w.w.Event(wakeupEvent{})
default:
}
if w.dead {
break
}
w.draw()
} }
} return nil
func (w *window) Invalidate() {
select {
case w.wakeups <- struct{}{}:
default:
return
}
w.disp.wakeup()
}
func (w *window) Run(f func()) {
f()
}
func (w *window) Frame(frame *op.Ops) {
w.w.ProcessFrame(frame, nil)
} }
// bindDataDevice initializes the dataDev field if and only if both // bindDataDevice initializes the dataDev field if and only if both
@@ -1446,21 +1385,13 @@ func (d *wlDisplay) bindDataDevice() {
} }
} }
func (d *wlDisplay) dispatch() error { func (d *wlDisplay) dispatch(p *poller) error {
// wl_display_prepare_read records the current thread for
// use in wl_display_read_events or wl_display_cancel_events.
runtime.LockOSThread()
defer runtime.UnlockOSThread()
dispfd := C.wl_display_get_fd(d.disp) dispfd := C.wl_display_get_fd(d.disp)
// Poll for events and notifications. // Poll for events and notifications.
pollfds := append(d.poller.pollfds[:0], pollfds := append(p.pollfds[:0],
syscall.PollFd{Fd: int32(dispfd), Events: syscall.POLLIN | syscall.POLLERR}, syscall.PollFd{Fd: int32(dispfd), Events: syscall.POLLIN | syscall.POLLERR},
syscall.PollFd{Fd: int32(d.notify.read), Events: syscall.POLLIN | syscall.POLLERR}, syscall.PollFd{Fd: int32(d.notify.read), Events: syscall.POLLIN | syscall.POLLERR},
) )
for C.wl_display_prepare_read(d.disp) != 0 {
C.wl_display_dispatch_pending(d.disp)
}
dispFd := &pollfds[0] dispFd := &pollfds[0]
if ret, err := C.wl_display_flush(d.disp); ret < 0 { if ret, err := C.wl_display_flush(d.disp); ret < 0 {
if err != syscall.EAGAIN { if err != syscall.EAGAIN {
@@ -1471,25 +1402,11 @@ func (d *wlDisplay) dispatch() error {
dispFd.Events |= syscall.POLLOUT dispFd.Events |= syscall.POLLOUT
} }
if _, err := syscall.Poll(pollfds, -1); err != nil && err != syscall.EINTR { if _, err := syscall.Poll(pollfds, -1); err != nil && err != syscall.EINTR {
C.wl_display_cancel_read(d.disp)
return fmt.Errorf("wayland: poll failed: %v", err) return fmt.Errorf("wayland: poll failed: %v", err)
} }
if dispFd.Revents&(syscall.POLLERR|syscall.POLLHUP) != 0 {
C.wl_display_cancel_read(d.disp)
return errors.New("wayland: display file descriptor gone")
}
// Handle events.
if dispFd.Revents&syscall.POLLIN != 0 {
if ret, err := C.wl_display_read_events(d.disp); ret < 0 {
return fmt.Errorf("wayland: wl_display_read_events failed: %v", err)
}
C.wl_display_dispatch_pending(d.disp)
} else {
C.wl_display_cancel_read(d.disp)
}
// Clear notifications. // Clear notifications.
for { for {
_, err := syscall.Read(d.notify.read, d.poller.buf[:]) _, err := syscall.Read(d.notify.read, p.buf[:])
if err == syscall.EAGAIN { if err == syscall.EAGAIN {
break break
} }
@@ -1497,15 +1414,29 @@ func (d *wlDisplay) dispatch() error {
return fmt.Errorf("wayland: read from notify pipe failed: %v", err) return fmt.Errorf("wayland: read from notify pipe failed: %v", err)
} }
} }
// Handle events
switch {
case dispFd.Revents&syscall.POLLIN != 0:
if ret, err := C.wl_display_dispatch(d.disp); ret < 0 {
return fmt.Errorf("wayland: wl_display_dispatch failed: %v", err)
}
case dispFd.Revents&(syscall.POLLERR|syscall.POLLHUP) != 0:
return errors.New("wayland: display file descriptor gone")
}
d.repeat.Repeat(d) d.repeat.Repeat(d)
return nil return nil
} }
func (w *window) Wakeup() {
select {
case w.wakeups <- struct{}{}:
default:
}
w.disp.wakeup()
}
func (w *window) SetAnimating(anim bool) { func (w *window) SetAnimating(anim bool) {
w.animating = anim w.animating = anim
if anim {
w.draw(false)
}
} }
// Wakeup wakes up the event loop through the notification pipe. // Wakeup wakes up the event loop through the notification pipe.
@@ -1517,10 +1448,6 @@ func (d *wlDisplay) wakeup() {
} }
func (w *window) destroy() { func (w *window) destroy() {
if w.lastFrameCallback != nil {
C.wl_callback_destroy(w.lastFrameCallback)
w.lastFrameCallback = nil
}
if w.cursor.surf != nil { if w.cursor.surf != nil {
C.wl_surface_destroy(w.cursor.surf) C.wl_surface_destroy(w.cursor.surf)
} }
@@ -1645,15 +1572,7 @@ func (w *window) flushScroll() {
if total == (f32.Point{}) { if total == (f32.Point{}) {
return return
} }
if w.scroll.steps == (image.Point{}) { w.w.Event(pointer.Event{
w.fling.xExtrapolation.SampleDelta(w.scroll.time, -w.scroll.dist.X)
w.fling.yExtrapolation.SampleDelta(w.scroll.time, -w.scroll.dist.Y)
}
// Zero scroll distance prior to calling ProcessEvent, otherwise we may recursively
// re-process the scroll distance.
w.scroll.dist = f32.Point{}
w.scroll.steps = image.Point{}
w.ProcessEvent(pointer.Event{
Kind: pointer.Scroll, Kind: pointer.Scroll,
Source: pointer.Mouse, Source: pointer.Mouse,
Buttons: w.pointerBtns, Buttons: w.pointerBtns,
@@ -1662,6 +1581,12 @@ func (w *window) flushScroll() {
Time: w.scroll.time, Time: w.scroll.time,
Modifiers: w.disp.xkb.Modifiers(), Modifiers: w.disp.xkb.Modifiers(),
}) })
if w.scroll.steps == (image.Point{}) {
w.fling.xExtrapolation.SampleDelta(w.scroll.time, -w.scroll.dist.X)
w.fling.yExtrapolation.SampleDelta(w.scroll.time, -w.scroll.dist.Y)
}
w.scroll.dist = f32.Point{}
w.scroll.steps = image.Point{}
} }
func (w *window) onPointerMotion(x, y C.wl_fixed_t, t C.uint32_t) { func (w *window) onPointerMotion(x, y C.wl_fixed_t, t C.uint32_t) {
@@ -1670,7 +1595,7 @@ func (w *window) onPointerMotion(x, y C.wl_fixed_t, t C.uint32_t) {
X: fromFixed(x) * float32(w.scale), X: fromFixed(x) * float32(w.scale),
Y: fromFixed(y) * float32(w.scale), Y: fromFixed(y) * float32(w.scale),
} }
w.ProcessEvent(pointer.Event{ w.w.Event(pointer.Event{
Kind: pointer.Move, Kind: pointer.Move,
Position: w.lastPos, Position: w.lastPos,
Buttons: w.pointerBtns, Buttons: w.pointerBtns,
@@ -1691,8 +1616,7 @@ func (w *window) systemGesture() (*C.struct_wl_cursor, C.uint32_t) {
if w.config.Mode != Windowed || w.config.Decorated { if w.config.Mode != Windowed || w.config.Decorated {
return nil, 0 return nil, 0
} }
_, cfg := w.getConfig() border := w.w.w.metric.Dp(3)
border := cfg.Dp(3)
x, y, size := int(w.lastPos.X), int(w.lastPos.Y), w.config.Size x, y, size := int(w.lastPos.X), int(w.lastPos.Y), w.config.Size
north := y <= border north := y <= border
south := y >= size.Y-border south := y >= size.Y-border
@@ -1746,10 +1670,13 @@ func (w *window) updateOutputs() {
if found && scale != w.scale { if found && scale != w.scale {
w.scale = scale w.scale = scale
C.wl_surface_set_buffer_scale(w.surf, C.int32_t(w.scale)) C.wl_surface_set_buffer_scale(w.surf, C.int32_t(w.scale))
w.draw(true) w.redraw = true
} }
if found { if !found {
w.draw(true) w.setStage(system.StagePaused)
} else {
w.setStage(system.StageRunning)
w.redraw = true
} }
} }
@@ -1761,10 +1688,7 @@ func (w *window) getConfig() (image.Point, unit.Metric) {
} }
} }
func (w *window) draw(sync bool) { func (w *window) draw() {
if !w.configured {
return
}
w.flushScroll() w.flushScroll()
size, cfg := w.getConfig() size, cfg := w.getConfig()
if cfg == (unit.Metric{}) { if cfg == (unit.Metric{}) {
@@ -1772,9 +1696,11 @@ func (w *window) draw(sync bool) {
} }
if size != w.config.Size { if size != w.config.Size {
w.config.Size = size w.config.Size = size
w.ProcessEvent(ConfigEvent{Config: w.config}) w.w.Event(ConfigEvent{Config: w.config})
} }
anim := w.animating || w.fling.anim.Active() anim := w.animating || w.fling.anim.Active()
sync := w.redraw
w.redraw = false
// Draw animation only when not waiting for frame callback. // Draw animation only when not waiting for frame callback.
redrawAnim := anim && w.lastFrameCallback == nil redrawAnim := anim && w.lastFrameCallback == nil
if !redrawAnim && !sync { if !redrawAnim && !sync {
@@ -1785,8 +1711,8 @@ func (w *window) draw(sync bool) {
// Use the surface as listener data for gio_onFrameDone. // Use the surface as listener data for gio_onFrameDone.
C.wl_callback_add_listener(w.lastFrameCallback, &C.gio_callback_listener, unsafe.Pointer(w.surf)) C.wl_callback_add_listener(w.lastFrameCallback, &C.gio_callback_listener, unsafe.Pointer(w.surf))
} }
w.ProcessEvent(frameEvent{ w.w.Event(frameEvent{
FrameEvent: FrameEvent{ FrameEvent: system.FrameEvent{
Now: time.Now(), Now: time.Now(),
Size: w.config.Size, Size: w.config.Size,
Metric: cfg, Metric: cfg,
@@ -1795,6 +1721,14 @@ func (w *window) draw(sync bool) {
}) })
} }
func (w *window) setStage(s system.Stage) {
if s == w.stage {
return
}
w.stage = s
w.w.Event(system.StageEvent{Stage: s})
}
func (w *window) display() *C.struct_wl_display { func (w *window) display() *C.struct_wl_display {
return w.disp.disp return w.disp.disp
} }
@@ -1886,10 +1820,6 @@ func newWLDisplay() (*wlDisplay, error) {
} }
func (d *wlDisplay) destroy() { func (d *wlDisplay) destroy() {
if d.readClipClose != nil {
close(d.readClipClose)
d.readClipClose = nil
}
if d.notify.write != 0 { if d.notify.write != 0 {
syscall.Close(d.notify.write) syscall.Close(d.notify.write)
d.notify.write = 0 d.notify.write = 0
@@ -1931,7 +1861,6 @@ func (d *wlDisplay) destroy() {
if d.disp != nil { if d.disp != nil {
C.wl_display_disconnect(d.disp) C.wl_display_disconnect(d.disp)
callbackDelete(unsafe.Pointer(d.disp)) callbackDelete(unsafe.Pointer(d.disp))
d.disp = nil
} }
} }
+166 -485
View File
@@ -5,12 +5,7 @@ package app
import ( import (
"errors" "errors"
"fmt" "fmt"
"gioui.org/io/transfer"
syscall "golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
"image" "image"
"io"
"os"
"runtime" "runtime"
"sort" "sort"
"strings" "strings"
@@ -20,45 +15,47 @@ import (
"unicode/utf8" "unicode/utf8"
"unsafe" "unsafe"
syscall "golang.org/x/sys/windows"
"gioui.org/app/internal/windows" "gioui.org/app/internal/windows"
"gioui.org/op"
"gioui.org/unit" "gioui.org/unit"
gowindows "golang.org/x/sys/windows" gowindows "golang.org/x/sys/windows"
"gioui.org/f32" "gioui.org/f32"
"gioui.org/io/event" "gioui.org/io/clipboard"
"gioui.org/io/key" "gioui.org/io/key"
"gioui.org/io/pointer" "gioui.org/io/pointer"
"gioui.org/io/system" "gioui.org/io/system"
) )
type Win32ViewEvent struct { type ViewEvent struct {
HWND uintptr HWND uintptr
} }
type window struct { type window struct {
hwnd syscall.Handle hwnd syscall.Handle
hdc syscall.Handle hdc syscall.Handle
w *callbacks w *callbacks
stage system.Stage
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
focused bool
borderSize image.Point borderSize image.Point
config Config config Config
// frameDims stores the last seen window frame width and height.
frameDims image.Point
loop *eventLoop
} }
const _WM_WAKEUP = windows.WM_USER + iota const _WM_WAKEUP = windows.WM_USER + iota
const copyDataURLType = 0xffffff00
type gpuAPI struct { type gpuAPI struct {
priority int priority int
initializer func(w *window) (context, error) initializer func(w *window) (context, error)
@@ -84,42 +81,39 @@ var resources struct {
} }
func osMain() { func osMain() {
processURLEvent(startupURI())
select {} select {}
} }
func newWindow(win *callbacks, options []Option) { func newWindow(window *callbacks, options []Option) error {
done := make(chan struct{}) cerr := make(chan error)
go func() { go func() {
// GetMessage and PeekMessage can filter on a window HWND, but // GetMessage and PeekMessage can filter on a window HWND, but
// then thread-specific messages such as WM_QUIT are ignored. // then thread-specific messages such as WM_QUIT are ignored.
// Instead lock the thread so window messages arrive through // Instead lock the thread so window messages arrive through
// unfiltered GetMessage calls. // unfiltered GetMessage calls.
runtime.LockOSThread() runtime.LockOSThread()
w, err := createNativeWindow()
w := &window{
w: win,
}
w.loop = newEventLoop(w.w, w.wakeup)
w.w.SetDriver(w)
err := w.init()
done <- struct{}{}
if err != nil { if err != nil {
w.ProcessEvent(DestroyEvent{Err: err}) cerr <- err
return return
} }
cerr <- nil
winMap.Store(w.hwnd, w) winMap.Store(w.hwnd, w)
defer winMap.Delete(w.hwnd) defer winMap.Delete(w.hwnd)
w.w = window
w.w.SetDriver(w)
w.w.Event(ViewEvent{HWND: uintptr(w.hwnd)})
w.Configure(options) w.Configure(options)
w.ProcessEvent(Win32ViewEvent{HWND: uintptr(w.hwnd)})
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,
// set it here to show the cursor. // set it here to show the cursor.
w.SetCursor(pointer.CursorDefault) w.SetCursor(pointer.CursorDefault)
w.runLoop() if err := w.loop(); err != nil {
panic(err)
}
}() }()
<-done return <-cerr
} }
// initResources initializes the resources global. // initResources initializes the resources global.
@@ -136,19 +130,13 @@ func initResources() error {
} }
resources.cursor = c resources.cursor = c
icon, _ := windows.LoadImage(hInst, iconID, windows.IMAGE_ICON, 0, 0, windows.LR_DEFAULTSIZE|windows.LR_SHARED) icon, _ := windows.LoadImage(hInst, iconID, windows.IMAGE_ICON, 0, 0, windows.LR_DEFAULTSIZE|windows.LR_SHARED)
appid, err := syscall.UTF16PtrFromString(ID)
if err != nil {
return err
}
wcls := windows.WndClassEx{ wcls := windows.WndClassEx{
CbSize: uint32(unsafe.Sizeof(windows.WndClassEx{})), CbSize: uint32(unsafe.Sizeof(windows.WndClassEx{})),
Style: windows.CS_HREDRAW | windows.CS_VREDRAW | windows.CS_OWNDC, Style: windows.CS_HREDRAW | windows.CS_VREDRAW | windows.CS_OWNDC,
LpfnWndProc: syscall.NewCallback(windowProc), LpfnWndProc: syscall.NewCallback(windowProc),
HInstance: hInst, HInstance: hInst,
HIcon: icon, HIcon: icon,
LpszClassName: appid, LpszClassName: syscall.StringToUTF16Ptr("GioWindow"),
} }
cls, err := windows.RegisterClassEx(&wcls) cls, err := windows.RegisterClassEx(&wcls)
if err != nil { if err != nil {
@@ -160,13 +148,13 @@ func initResources() error {
const dwExStyle = windows.WS_EX_APPWINDOW | windows.WS_EX_WINDOWEDGE const dwExStyle = windows.WS_EX_APPWINDOW | windows.WS_EX_WINDOWEDGE
func (w *window) init() error { func createNativeWindow() (*window, error) {
var resErr error var resErr error
resources.once.Do(func() { resources.once.Do(func() {
resErr = initResources() resErr = initResources()
}) })
if resErr != nil { if resErr != nil {
return resErr return nil, resErr
} }
const dwStyle = windows.WS_OVERLAPPEDWINDOW const dwStyle = windows.WS_OVERLAPPEDWINDOW
@@ -182,56 +170,33 @@ func (w *window) init() error {
resources.handle, resources.handle,
0) 0)
if err != nil { if err != nil {
return err return nil, err
} }
if err := windows.RegisterTouchWindow(hwnd, 0); err != nil { w := &window{
return err hwnd: hwnd,
}
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) return nil, err
return err
} }
w.hwnd = hwnd return w, 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) w.w.Event(ConfigEvent{Config: w.config})
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.draw(true)
} }
func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr { func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr {
@@ -272,7 +237,7 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
e.State = key.Release e.State = key.Release
} }
w.ProcessEvent(e) w.w.Event(e)
if (wParam == windows.VK_F10) && (msg == windows.WM_SYSKEYDOWN || msg == windows.WM_SYSKEYUP) { if (wParam == windows.VK_F10) && (msg == windows.WM_SYSKEYDOWN || msg == windows.WM_SYSKEYUP) {
// Reserve F10 for ourselves, and don't let it open the system menu. Other Windows programs // Reserve F10 for ourselves, and don't let it open the system menu. Other Windows programs
@@ -280,42 +245,36 @@ 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.w.Event(pointer.Event{
Kind: pointer.Cancel, Kind: pointer.Cancel,
}) })
case windows.WM_SETFOCUS: case windows.WM_SETFOCUS:
w.config.Focused = true w.focused = true
w.ProcessEvent(ConfigEvent{Config: w.config}) w.w.Event(key.FocusEvent{Focus: true})
case windows.WM_KILLFOCUS: case windows.WM_KILLFOCUS:
w.config.Focused = false w.focused = false
w.ProcessEvent(ConfigEvent{Config: w.config}) w.w.Event(key.FocusEvent{Focus: false})
case windows.WM_NCACTIVATE:
if w.stage >= system.StageInactive {
if wParam == windows.TRUE {
w.setStage(system.StageRunning)
} else {
w.setStage(system.StageInactive)
}
}
case windows.WM_NCHITTEST: case windows.WM_NCHITTEST:
if w.config.Decorated { if w.config.Decorated {
// Let the system handle it. // Let the system handle it.
@@ -325,14 +284,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.w.Event(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.w.Event(ViewEvent{})
w.ProcessEvent(DestroyEvent{}) w.w.Event(system.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
@@ -340,7 +309,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.
@@ -359,37 +327,46 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
// Adjust window position to avoid the extra padding in maximized // Adjust window position to avoid the extra padding in maximized
// state. See https://devblogs.microsoft.com/oldnewthing/20150304-00/?p=44543. // state. See https://devblogs.microsoft.com/oldnewthing/20150304-00/?p=44543.
// Note that trying to do the adjustment in WM_GETMINMAXINFO is ignored by Windows. // Note that trying to do the adjustment in WM_GETMINMAXINFO is ignored by Windows.
szp := (*windows.NCCalcSizeParams)(unsafe.Pointer(lParam)) szp := (*windows.NCCalcSizeParams)(unsafe.Pointer(uintptr(lParam)))
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_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
w.setStage(system.StagePaused)
case windows.SIZE_MAXIMIZED:
w.config.Mode = Maximized
w.setStage(system.StageRunning)
case windows.SIZE_RESTORED:
if w.config.Mode != Fullscreen {
w.config.Mode = Windowed
}
w.setStage(system.StageRunning)
}
case windows.WM_GETMINMAXINFO: case windows.WM_GETMINMAXINFO:
mm := (*windows.MinMaxInfo)(unsafe.Pointer(lParam)) mm := (*windows.MinMaxInfo)(unsafe.Pointer(uintptr(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
@@ -400,8 +377,7 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
return windows.TRUE return windows.TRUE
} }
case _WM_WAKEUP: case _WM_WAKEUP:
w.loop.Wakeup() w.w.Event(wakeupEvent{})
w.loop.FlushEvents()
case windows.WM_IME_STARTCOMPOSITION: case windows.WM_IME_STARTCOMPOSITION:
imc := windows.ImmGetContext(w.hwnd) imc := windows.ImmGetContext(w.hwnd)
if imc == 0 { if imc == 0 {
@@ -456,20 +432,6 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr
case windows.WM_IME_ENDCOMPOSITION: case windows.WM_IME_ENDCOMPOSITION:
w.w.SetComposingRegion(key.Range{Start: -1, End: -1}) w.w.SetComposingRegion(key.Range{Start: -1, End: -1})
return windows.TRUE return windows.TRUE
case windows.WM_COPYDATA:
data := (*windows.CopyDataStruct)(unsafe.Pointer(lParam))
switch data.DwData {
case copyDataURLType:
if schemesURI == "" {
return windows.TRUE
}
uri := syscall.UTF16PtrToString((*uint16)(unsafe.Pointer(data.LpData)))
if processURLEvent(uri) {
w.Perform(system.ActionRaise)
}
return windows.TRUE
}
} }
return windows.DefWindowProc(hwnd, msg, wParam, lParam) return windows.DefWindowProc(hwnd, msg, wParam, lParam)
@@ -495,6 +457,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
@@ -531,28 +496,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.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) w.w.Event(pointer.Event{
p := f32.Point{X: float32(np.X), Y: float32(np.Y)}
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,
}) })
} }
@@ -563,12 +534,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.
@@ -587,11 +552,11 @@ func (w *window) scrollEvent(wParam, lParam uintptr, horizontal bool, kmods key.
sp.Y = -dist sp.Y = -dist
} }
} }
w.ProcessEvent(pointer.Event{ w.w.Event(pointer.Event{
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(),
@@ -599,19 +564,18 @@ func (w *window) scrollEvent(wParam, lParam uintptr, horizontal bool, kmods key.
} }
// Adapted from https://blogs.msdn.microsoft.com/oldnewthing/20060126-00/?p=32513/ // Adapted from https://blogs.msdn.microsoft.com/oldnewthing/20060126-00/?p=32513/
func (w *window) runLoop() { func (w *window) loop() error {
msg := new(windows.Msg) msg := new(windows.Msg)
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
} }
switch ret := windows.GetMessage(msg, 0, 0, 0); ret { switch ret := windows.GetMessage(msg, 0, 0, 0); ret {
case -1: case -1:
panic(errors.New("GetMessage failed")) return errors.New("GetMessage failed")
case 0: case 0:
// WM_QUIT received. // WM_QUIT received.
break loop break loop
@@ -619,6 +583,7 @@ loop:
windows.TranslateMessage(msg) windows.TranslateMessage(msg)
windows.DispatchMessage(msg) windows.DispatchMessage(msg)
} }
return nil
} }
func (w *window) EditorStateChanged(old, new editorState) { func (w *window) EditorStateChanged(old, new editorState) {
@@ -636,41 +601,27 @@ func (w *window) SetAnimating(anim bool) {
w.animating = anim w.animating = anim
} }
func (w *window) ProcessEvent(e event.Event) { func (w *window) Wakeup() {
w.w.ProcessEvent(e)
w.loop.FlushEvents()
}
func (w *window) Event() event.Event {
return w.loop.Event()
}
func (w *window) Invalidate() {
w.loop.Invalidate()
}
func (w *window) Run(f func()) {
w.loop.Run(f)
}
func (w *window) Frame(frame *op.Ops) {
w.loop.Frame(frame)
}
func (w *window) wakeup() {
if err := windows.PostMessage(w.hwnd, _WM_WAKEUP, 0, 0); err != nil { if err := windows.PostMessage(w.hwnd, _WM_WAKEUP, 0, 0); err != nil {
panic(err) panic(err)
} }
} }
func (w *window) setStage(s system.Stage) {
if s != w.stage {
w.stage = s
w.w.Event(system.StageEvent{Stage: s})
}
}
func (w *window) draw(sync bool) { func (w *window) draw(sync bool) {
if w.config.Size.X == 0 || w.config.Size.Y == 0 { if w.config.Size.X == 0 || w.config.Size.Y == 0 {
return return
} }
dpi := windows.GetWindowDPI(w.hwnd) dpi := windows.GetWindowDPI(w.hwnd)
cfg := configForDPI(dpi) cfg := configForDPI(dpi)
w.ProcessEvent(frameEvent{ w.w.Event(frameEvent{
FrameEvent: FrameEvent{ FrameEvent: system.FrameEvent{
Now: time.Now(), Now: time.Now(),
Size: w.config.Size, Size: w.config.Size,
Metric: cfg, Metric: cfg,
@@ -716,25 +667,15 @@ func (w *window) readClipboard() error {
} }
defer windows.GlobalUnlock(mem) defer windows.GlobalUnlock(mem)
content := gowindows.UTF16PtrToString((*uint16)(unsafe.Pointer(ptr))) content := gowindows.UTF16PtrToString((*uint16)(unsafe.Pointer(ptr)))
w.ProcessEvent(transfer.DataEvent{ w.w.Event(clipboard.Event{Text: content})
Type: "application/text",
Open: func() io.ReadCloser {
return io.NopCloser(strings.NewReader(content))
},
})
return nil return nil
} }
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
@@ -742,7 +683,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
@@ -757,13 +698,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{
@@ -773,31 +714,29 @@ 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(s string) {
w.writeClipboard(string(s)) w.writeClipboard(s)
} }
func (w *window) writeClipboard(s string) error { func (w *window) writeClipboard(s string) error {
@@ -925,11 +864,11 @@ func (w *window) raise() {
windows.SWP_NOMOVE|windows.SWP_NOSIZE|windows.SWP_SHOWWINDOW) windows.SWP_NOMOVE|windows.SWP_NOSIZE|windows.SWP_SHOWWINDOW)
} }
func convertKeyCode(code uintptr) (key.Name, bool) { func convertKeyCode(code uintptr) (string, bool) {
if '0' <= code && code <= '9' || 'A' <= code && code <= 'Z' { if '0' <= code && code <= '9' || 'A' <= code && code <= 'Z' {
return key.Name(rune(code)), true return string(rune(code)), true
} }
var r key.Name var r string
switch code { switch code {
case windows.VK_ESCAPE: case windows.VK_ESCAPE:
@@ -1029,262 +968,4 @@ func configForDPI(dpi int) unit.Metric {
} }
} }
func (Win32ViewEvent) implementsViewEvent() {} func (_ ViewEvent) ImplementsEvent() {}
func (Win32ViewEvent) ImplementsEvent() {}
func (w Win32ViewEvent) Valid() bool {
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
}
// schemesURI is a list of schemes, comma separated, that must be
// defined using -X compiler ldflag, that used in gogio.
var schemesURI string
func init() {
if schemesURI == "" {
return
}
currentSchemes := strings.Split(schemesURI, ",")
oldSchemes := registeredSchemes(ID)
for _, s := range currentSchemes {
for i, o := range oldSchemes {
if s == o {
oldSchemes = append(oldSchemes[:i], oldSchemes[i+1:]...)
break
}
}
}
if len(oldSchemes) > 0 {
go unregisterSchemes(ID, oldSchemes)
}
if len(currentSchemes) == 0 {
return
}
// On Windows, launching the app using a URI will start a new instance of the app,
// a new window. That behavior, by default, doesn't align with iOS/Android/macOS, where
// the deeplink sends the event to the running app (if any). We are emulating it.
if hwnd, _ := windows.FindWindow(ID); hwnd != 0 {
if u := startupURI(); u != "" {
broadcastURI(hwnd, u)
}
os.Exit(0)
return
}
go registerSchemes(ID, currentSchemes)
}
func startupURI() string {
if len(os.Args) == 3 && os.Args[1] == "-gio_launch_url" {
return os.Args[2]
}
return ""
}
func processURLEvent(rawurl string) bool {
if rawurl == "" {
return false
}
evt, err := newURLEvent(rawurl)
if err != nil {
return false
}
for _, scheme := range strings.Split(schemesURI, ",") {
if strings.EqualFold(scheme, evt.URL.Scheme) {
processGlobalEvent(evt)
return true
}
}
return false
}
func broadcastURI(hwnd syscall.Handle, uri string) {
data, err := syscall.UTF16FromString(uri)
if err != nil {
return // Only happens if uri contains NULL character.
}
pinner := new(runtime.Pinner)
defer pinner.Unpin()
pinner.Pin(unsafe.Pointer(&data[0]))
msg := &windows.CopyDataStruct{
DwData: copyDataURLType,
CbData: uint32(len(data) * int(unsafe.Sizeof(data[0]))),
LpData: uintptr(unsafe.Pointer(unsafe.SliceData(data))),
}
pinner.Pin(unsafe.Pointer(msg))
// SendMessage blocks until the message is processed.
windows.SendMessage(hwnd, windows.WM_COPYDATA, 0, uintptr(unsafe.Pointer(msg)))
}
func registeredSchemes(appid string) []string {
meta, err := registry.OpenKey(registry.CURRENT_USER, `Software\\`+appid, registry.ALL_ACCESS)
if err != nil {
return nil
}
defer meta.Close()
schemes, _, _ := meta.GetStringsValue("URISchemes")
return schemes
}
func registerSchemes(appid string, schemes []string) error {
reg := func(scheme string) error {
key, existent, err := registry.CreateKey(registry.CURRENT_USER, `Software\\Classes\\`+scheme, registry.ALL_ACCESS)
if err != nil {
return err
}
defer key.Close()
if existent {
// Check if the existent key belongs to the current application
id, _, err := key.GetStringValue("appid")
if err != nil || id != appid {
return fmt.Errorf("scheme %s already registered by another application", scheme)
}
}
path, err := os.Executable()
if err != nil {
return err
}
if err = key.SetStringValue("", "URL:"+scheme+" Protocol"); err != nil {
return err
}
if err = key.SetStringValue("URL Protocol", ""); err != nil {
return err
}
if err = key.SetStringValue("appid", appid); err != nil {
return err
}
icon, _, err := registry.CreateKey(key, `DefaultIcon`, registry.ALL_ACCESS)
if err != nil {
return err
}
defer icon.Close()
if err = icon.SetStringValue("", `"`+path+`",1`); err != nil {
return err
}
cmd, _, err := registry.CreateKey(key, `shell\\open\\command`, registry.ALL_ACCESS)
if err != nil {
return err
}
defer cmd.Close()
if err = cmd.SetStringValue("", `"`+path+`" -gio_launch_url "%1"`); err != nil {
return err
}
return nil
}
for _, scheme := range schemes {
if scheme == "" {
continue // just in case
}
if err := reg(scheme); err != nil {
return err
}
}
meta, _, err := registry.CreateKey(registry.CURRENT_USER, `Software\\`+appid, registry.ALL_ACCESS)
if err != nil {
return err
}
defer meta.Close()
if err = meta.SetStringsValue("URISchemes", schemes); err != nil {
return err
}
return nil
}
func unregisterSchemes(appid string, schemes []string) {
classes, err := registry.OpenKey(registry.CURRENT_USER, `Software\\Classes`, registry.ALL_ACCESS)
if err != nil {
return
}
defer classes.Close()
for _, scheme := range schemes {
if scheme == "" {
continue // just in case
}
key, err := registry.OpenKey(classes, scheme, registry.ALL_ACCESS)
if err != nil {
continue
}
id, _, err := key.GetStringValue("appid")
if err == nil && id != appid {
continue
}
for _, k := range []string{`DefaultIcon`, `shell\\open\\command`, `shell\\open`, `shell`} {
registry.DeleteKey(key, k)
}
if err := key.Close(); err != nil {
continue
}
registry.DeleteKey(classes, scheme)
}
}
+86 -117
View File
@@ -26,25 +26,20 @@ package app
*/ */
import "C" import "C"
import ( import (
"errors" "errors"
"fmt" "fmt"
"image" "image"
"io"
"strconv" "strconv"
"strings"
"sync" "sync"
"time" "time"
"unsafe" "unsafe"
"gioui.org/f32" "gioui.org/f32"
"gioui.org/io/event" "gioui.org/io/clipboard"
"gioui.org/io/key" "gioui.org/io/key"
"gioui.org/io/pointer" "gioui.org/io/pointer"
"gioui.org/io/system" "gioui.org/io/system"
"gioui.org/io/transfer"
"gioui.org/op"
"gioui.org/unit" "gioui.org/unit"
syscall "golang.org/x/sys/unix" syscall "golang.org/x/sys/unix"
@@ -96,10 +91,12 @@ type x11Window struct {
// _NET_WM_STATE_MAXIMIZED_VERT // _NET_WM_STATE_MAXIMIZED_VERT
wmStateMaximizedVert C.Atom wmStateMaximizedVert C.Atom
} }
stage system.Stage
metric unit.Metric metric unit.Metric
notify struct { notify struct {
read, write int read, write int
} }
dead bool
animating bool animating bool
@@ -112,8 +109,6 @@ type x11Window struct {
config Config config Config
wakeups chan struct{} wakeups chan struct{}
handler x11EventHandler
buf [100]byte
} }
var ( var (
@@ -156,8 +151,8 @@ func (w *x11Window) ReadClipboard() {
C.XConvertSelection(w.x, w.atoms.clipboard, w.atoms.utf8string, w.atoms.clipboardContent, w.xw, C.CurrentTime) C.XConvertSelection(w.x, w.atoms.clipboard, w.atoms.utf8string, w.atoms.clipboardContent, w.xw, C.CurrentTime)
} }
func (w *x11Window) WriteClipboard(mime string, s []byte) { func (w *x11Window) WriteClipboard(s string) {
w.clipboard.content = s w.clipboard.content = []byte(s)
C.XSetSelectionOwner(w.x, w.atoms.clipboard, w.xw, C.CurrentTime) C.XSetSelectionOwner(w.x, w.atoms.clipboard, w.xw, C.CurrentTime)
C.XSetSelectionOwner(w.x, w.atoms.primary, w.xw, C.CurrentTime) C.XSetSelectionOwner(w.x, w.atoms.primary, w.xw, C.CurrentTime)
} }
@@ -237,7 +232,7 @@ func (w *x11Window) Configure(options []Option) {
if cnf.Decorated != prev.Decorated { if cnf.Decorated != prev.Decorated {
w.config.Decorated = cnf.Decorated w.config.Decorated = cnf.Decorated
} }
w.ProcessEvent(ConfigEvent{Config: w.config}) w.w.Event(ConfigEvent{Config: w.config})
} }
func (w *x11Window) setTitle(prev, cnf Config) { func (w *x11Window) setTitle(prev, cnf Config) {
@@ -380,36 +375,7 @@ func (w *x11Window) sendWMStateEvent(action C.long, atom1, atom2 C.ulong) {
var x11OneByte = make([]byte, 1) var x11OneByte = make([]byte, 1)
func (w *x11Window) ProcessEvent(e event.Event) { func (w *x11Window) Wakeup() {
w.w.ProcessEvent(e)
}
func (w *x11Window) shutdown(err error) {
w.ProcessEvent(X11ViewEvent{})
w.ProcessEvent(DestroyEvent{Err: err})
w.destroy()
}
func (w *x11Window) Event() event.Event {
for {
evt, ok := w.w.nextEvent()
if !ok {
w.dispatch()
continue
}
return evt
}
}
func (w *x11Window) Run(f func()) {
f()
}
func (w *x11Window) Frame(frame *op.Ops) {
w.w.ProcessFrame(frame, nil)
}
func (w *x11Window) Invalidate() {
select { select {
case w.wakeups <- struct{}{}: case w.wakeups <- struct{}{}:
default: default:
@@ -427,20 +393,16 @@ func (w *x11Window) window() (C.Window, int, int) {
return w.xw, w.config.Size.X, w.config.Size.Y return w.xw, w.config.Size.X, w.config.Size.Y
} }
func (w *x11Window) dispatch() { func (w *x11Window) setStage(s system.Stage) {
if w.x == nil { if s == w.stage {
// Only Invalidate can wake us up.
<-w.wakeups
w.w.Invalidate()
return return
} }
w.stage = s
w.w.Event(system.StageEvent{Stage: s})
}
select { func (w *x11Window) loop() {
case <-w.wakeups: h := x11EventHandler{w: w, xev: new(C.XEvent), text: make([]byte, 4)}
w.w.Invalidate()
default:
}
xfd := C.XConnectionNumber(w.x) xfd := C.XConnectionNumber(w.x)
// Poll for events and notifications. // Poll for events and notifications.
@@ -450,54 +412,60 @@ func (w *x11Window) dispatch() {
} }
xEvents := &pollfds[0].Revents xEvents := &pollfds[0].Revents
// Plenty of room for a backlog of notifications. // Plenty of room for a backlog of notifications.
buf := make([]byte, 100)
var syn, anim bool loop:
// Check for pending draw events before checking animation or blocking. for !w.dead {
// This fixes an issue on Xephyr where on startup XPending() > 0 but var syn, anim bool
// poll will still block. This also prevents no-op calls to poll. // Check for pending draw events before checking animation or blocking.
syn = w.handler.handleEvents() // This fixes an issue on Xephyr where on startup XPending() > 0 but
if w.x == nil { // poll will still block. This also prevents no-op calls to poll.
// handleEvents received a close request and destroyed the window. if syn = h.handleEvents(); !syn {
return anim = w.animating
} if !anim {
if !syn { // Clear poll events.
anim = w.animating *xEvents = 0
if !anim { // Wait for X event or gio notification.
// Clear poll events. if _, err := syscall.Poll(pollfds, -1); err != nil && err != syscall.EINTR {
*xEvents = 0 panic(fmt.Errorf("x11 loop: poll failed: %w", err))
// Wait for X event or gio notification. }
if _, err := syscall.Poll(pollfds, -1); err != nil && err != syscall.EINTR { switch {
panic(fmt.Errorf("x11 loop: poll failed: %w", err)) case *xEvents&syscall.POLLIN != 0:
} syn = h.handleEvents()
switch { if w.dead {
case *xEvents&syscall.POLLIN != 0: break loop
syn = w.handler.handleEvents() }
if w.x == nil { case *xEvents&(syscall.POLLERR|syscall.POLLHUP) != 0:
return break loop
} }
case *xEvents&(syscall.POLLERR|syscall.POLLHUP) != 0:
} }
} }
} // Clear notifications.
// Clear notifications. for {
for { _, err := syscall.Read(w.notify.read, buf)
_, err := syscall.Read(w.notify.read, w.buf[:]) if err == syscall.EAGAIN {
if err == syscall.EAGAIN { break
break }
if err != nil {
panic(fmt.Errorf("x11 loop: read from notify pipe failed: %w", err))
}
} }
if err != nil { select {
panic(fmt.Errorf("x11 loop: read from notify pipe failed: %w", err)) case <-w.wakeups:
w.w.Event(wakeupEvent{})
default:
}
if (anim || syn) && w.config.Size.X != 0 && w.config.Size.Y != 0 {
w.w.Event(frameEvent{
FrameEvent: system.FrameEvent{
Now: time.Now(),
Size: w.config.Size,
Metric: w.metric,
},
Sync: syn,
})
} }
}
if (anim || syn) && w.config.Size.X != 0 && w.config.Size.Y != 0 {
w.ProcessEvent(frameEvent{
FrameEvent: FrameEvent{
Now: time.Now(),
Size: w.config.Size,
Metric: w.metric,
},
Sync: syn,
})
} }
} }
@@ -516,7 +484,6 @@ func (w *x11Window) destroy() {
} }
C.XDestroyWindow(w.x, w.xw) C.XDestroyWindow(w.x, w.xw)
C.XCloseDisplay(w.x) C.XCloseDisplay(w.x)
w.x = nil
} }
// atom is a wrapper around XInternAtom. Callers should cache the result // atom is a wrapper around XInternAtom. Callers should cache the result
@@ -574,7 +541,7 @@ func (h *x11EventHandler) handleEvents() bool {
// There's no support for IME yet. // There's no support for IME yet.
w.w.EditorInsert(ee.Text) w.w.EditorInsert(ee.Text)
} else { } else {
w.ProcessEvent(e) w.w.Event(e)
} }
} }
case C.ButtonPress, C.ButtonRelease: case C.ButtonPress, C.ButtonRelease:
@@ -636,10 +603,10 @@ func (h *x11EventHandler) handleEvents() bool {
w.pointerBtns &^= btn w.pointerBtns &^= btn
} }
ev.Buttons = w.pointerBtns ev.Buttons = w.pointerBtns
w.ProcessEvent(ev) w.w.Event(ev)
case C.MotionNotify: case C.MotionNotify:
mevt := (*C.XMotionEvent)(unsafe.Pointer(xev)) mevt := (*C.XMotionEvent)(unsafe.Pointer(xev))
w.ProcessEvent(pointer.Event{ w.w.Event(pointer.Event{
Kind: pointer.Move, Kind: pointer.Move,
Source: pointer.Mouse, Source: pointer.Mouse,
Buttons: w.pointerBtns, Buttons: w.pointerBtns,
@@ -654,16 +621,14 @@ func (h *x11EventHandler) handleEvents() bool {
// redraw only on the last expose event // redraw only on the last expose event
redraw = (*C.XExposeEvent)(unsafe.Pointer(xev)).count == 0 redraw = (*C.XExposeEvent)(unsafe.Pointer(xev)).count == 0
case C.FocusIn: case C.FocusIn:
w.config.Focused = true w.w.Event(key.FocusEvent{Focus: true})
w.ProcessEvent(ConfigEvent{Config: w.config})
case C.FocusOut: case C.FocusOut:
w.config.Focused = false w.w.Event(key.FocusEvent{Focus: false})
w.ProcessEvent(ConfigEvent{Config: w.config})
case C.ConfigureNotify: // window configuration change case C.ConfigureNotify: // window configuration change
cevt := (*C.XConfigureEvent)(unsafe.Pointer(xev)) cevt := (*C.XConfigureEvent)(unsafe.Pointer(xev))
if sz := image.Pt(int(cevt.width), int(cevt.height)); sz != w.config.Size { if sz := image.Pt(int(cevt.width), int(cevt.height)); sz != w.config.Size {
w.config.Size = sz w.config.Size = sz
w.ProcessEvent(ConfigEvent{Config: w.config}) w.w.Event(ConfigEvent{Config: w.config})
} }
// redraw will be done by a later expose event // redraw will be done by a later expose event
case C.SelectionNotify: case C.SelectionNotify:
@@ -685,12 +650,7 @@ func (h *x11EventHandler) handleEvents() bool {
break break
} }
str := C.GoStringN((*C.char)(unsafe.Pointer(text.value)), C.int(text.nitems)) str := C.GoStringN((*C.char)(unsafe.Pointer(text.value)), C.int(text.nitems))
w.ProcessEvent(transfer.DataEvent{ w.w.Event(clipboard.Event{Text: str})
Type: "application/text",
Open: func() io.ReadCloser {
return io.NopCloser(strings.NewReader(str))
},
})
case C.SelectionRequest: case C.SelectionRequest:
cevt := (*C.XSelectionRequestEvent)(unsafe.Pointer(xev)) cevt := (*C.XSelectionRequestEvent)(unsafe.Pointer(xev))
if (cevt.selection != w.atoms.clipboard && cevt.selection != w.atoms.primary) || cevt.property == C.None { if (cevt.selection != w.atoms.clipboard && cevt.selection != w.atoms.primary) || cevt.property == C.None {
@@ -744,7 +704,7 @@ func (h *x11EventHandler) handleEvents() bool {
cevt := (*C.XClientMessageEvent)(unsafe.Pointer(xev)) cevt := (*C.XClientMessageEvent)(unsafe.Pointer(xev))
switch *(*C.long)(unsafe.Pointer(&cevt.data)) { switch *(*C.long)(unsafe.Pointer(&cevt.data)) {
case C.long(w.atoms.evDelWindow): case C.long(w.atoms.evDelWindow):
w.shutdown(nil) w.dead = true
return false return false
} }
} }
@@ -752,7 +712,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
@@ -824,10 +786,8 @@ func newX11Window(gioWin *callbacks, options []Option) error {
wakeups: make(chan struct{}, 1), wakeups: make(chan struct{}, 1),
config: Config{Size: cnf.Size}, config: Config{Size: cnf.Size},
} }
w.handler = x11EventHandler{w: w, xev: new(C.XEvent), text: make([]byte, 4)}
w.notify.read = pipe[0] w.notify.read = pipe[0]
w.notify.write = pipe[1] w.notify.write = pipe[1]
w.w.SetDriver(w)
if err := w.updateXkbKeymap(); err != nil { if err := w.updateXkbKeymap(); err != nil {
w.destroy() w.destroy()
@@ -863,10 +823,19 @@ func newX11Window(gioWin *callbacks, options []Option) error {
// extensions // extensions
C.XSetWMProtocols(dpy, win, &w.atoms.evDelWindow, 1) C.XSetWMProtocols(dpy, win, &w.atoms.evDelWindow, 1)
// make the window visible on the screen go func() {
C.XMapWindow(dpy, win) w.w.SetDriver(w)
w.Configure(options)
w.ProcessEvent(X11ViewEvent{Display: unsafe.Pointer(dpy), Window: uintptr(win)}) // make the window visible on the screen
C.XMapWindow(dpy, win)
w.Configure(options)
w.w.Event(X11ViewEvent{Display: unsafe.Pointer(dpy), Window: uintptr(win)})
w.setStage(system.StageRunning)
w.loop()
w.w.Event(X11ViewEvent{})
w.w.Event(system.DestroyEvent{Err: nil})
w.destroy()
}()
return nil return nil
} }
-15
View File
@@ -1,15 +0,0 @@
// SPDX-License-Identifier: Unlicense OR MIT
/*
Package microphone implements permissions to access microphone hardware.
# Android
The following entries will be added to AndroidManifest.xml:
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
RECORD_AUDIO is a "dangerous" permission. See documentation for package
gioui.org/app/permission for more information.
*/
package microphone
+2 -1
View File
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: Unlicense OR MIT // SPDX-License-Identifier: Unlicense OR MIT
//go:build android || (darwin && ios) //go:build android || (darwin && ios)
// +build android darwin,ios
package app package app
@@ -24,6 +25,6 @@ func runMain() {
// Indirect call, since the linker does not know the address of main when // Indirect call, since the linker does not know the address of main when
// laying down this package. // laying down this package.
fn := mainMain fn := mainMain
go fn() fn()
}) })
} }
-13
View File
@@ -1,13 +0,0 @@
// SPDX-License-Identifier: Unlicense OR MIT
package app
// DestroyEvent is the last event sent through
// a window event channel.
type DestroyEvent struct {
// Err is nil for normal window closures. If a
// window is prematurely closed, Err is the cause.
Err error
}
func (DestroyEvent) ImplementsEvent() {}
+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
} }
+1
View File
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: Unlicense OR MIT // SPDX-License-Identifier: Unlicense OR MIT
//go:build !novulkan //go:build !novulkan
// +build !novulkan
package app package app
+915 -742
View File
File diff suppressed because it is too large Load Diff
+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
+67 -39
View File
@@ -1,58 +1,86 @@
{ {
"nodes": { "nodes": {
"android": {
"inputs": {
"devshell": "devshell",
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1659298920,
"narHash": "sha256-LgRMge8BZUG15EN43iDJOlnEMX1dvRprB7SaoNqgibU=",
"owner": "tadfisher",
"repo": "android-nixpkgs",
"rev": "d4f20a3cd4ce961bb23b48447457f6810d69ae5e",
"type": "github"
},
"original": {
"owner": "tadfisher",
"repo": "android-nixpkgs",
"type": "github"
}
},
"devshell": {
"inputs": {
"flake-utils": [
"android",
"nixpkgs"
],
"nixpkgs": [
"android",
"nixpkgs"
]
},
"locked": {
"lastModified": 1658746384,
"narHash": "sha256-CCJcoMOcXyZFrV1ag4XMTpAPjLWb4Anbv+ktXFI1ry0=",
"owner": "numtide",
"repo": "devshell",
"rev": "0ffc7937bb5e8141af03d462b468bd071eb18e1b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "devshell",
"type": "github"
}
},
"flake-utils": {
"locked": {
"lastModified": 1656928814,
"narHash": "sha256-RIFfgBuKz6Hp89yRr7+NR5tzIAbn52h8vT6vXkYjZoM=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "7e2a3b3dfd9af950a856d66b0a7d01e3c18aa249",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1747953325, "lastModified": 1659305579,
"narHash": "sha256-y2ZtlIlNTuVJUZCqzZAhIw5rrKP4DOSklev6c8PyCkQ=", "narHash": "sha256-SFeQTmh7hc9Y2fSkooHaoS8mDfPa04sfmUCtQ8MA6Pg=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "55d1f923c480dadce40f5231feb472e81b0bab48", "rev": "5857574d45925585baffde730369414319228a84",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "NixOS",
"ref": "nixos-25.05",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }
}, },
"root": { "root": {
"inputs": { "inputs": {
"nixpkgs": "nixpkgs", "android": "android",
"utils": "utils" "nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
} }
} }
}, },
+47 -38
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";
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 = jdk8.home;
packages = [
android-sdk
jdk8
clang
] ++ (if stdenv.isLinux then [
vulkan-headers vulkan-headers
libxkbcommon libxkbcommon
wayland wayland
@@ -42,13 +46,18 @@
xorg.libXcursor xorg.libXcursor
xorg.libXfixes xorg.libXfixes
libGL libGL
pkg-config pkgconfig
] 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
} }
} }
+62 -80
View File
@@ -18,7 +18,6 @@ import (
"gioui.org/f32" "gioui.org/f32"
"gioui.org/internal/fling" "gioui.org/internal/fling"
"gioui.org/io/event" "gioui.org/io/event"
"gioui.org/io/input"
"gioui.org/io/key" "gioui.org/io/key"
"gioui.org/io/pointer" "gioui.org/io/pointer"
"gioui.org/op" "gioui.org/op"
@@ -38,19 +37,15 @@ type Hover struct {
// Add the gesture to detect hovering over the current pointer area. // Add the gesture to detect hovering over the current pointer area.
func (h *Hover) Add(ops *op.Ops) { func (h *Hover) Add(ops *op.Ops) {
event.Op(ops, h) pointer.InputOp{
Tag: h,
Kinds: pointer.Enter | pointer.Leave,
}.Add(ops)
} }
// Update state and report whether a pointer is inside the area. // Update state and report whether a pointer is inside the area.
func (h *Hover) Update(q input.Source) bool { func (h *Hover) Update(q event.Queue) bool {
for { for _, ev := range q.Events(h) {
ev, ok := q.Event(pointer.Filter{
Target: h,
Kinds: pointer.Enter | pointer.Leave | pointer.Cancel,
})
if !ok {
break
}
e, ok := ev.(pointer.Event) e, ok := ev.(pointer.Event)
if !ok { if !ok {
continue continue
@@ -112,6 +107,7 @@ type Drag struct {
pressed bool pressed bool
pid pointer.ID pid pointer.ID
start f32.Point start f32.Point
grab bool
} }
// Scroll detects scroll gestures and reduces them to // Scroll detects scroll gestures and reduces them to
@@ -119,9 +115,11 @@ type Drag struct {
// movements as well as drag and fling touch gestures. // movements as well as drag and fling touch gestures.
type Scroll struct { type Scroll struct {
dragging bool dragging bool
axis Axis
estimator fling.Extrapolation estimator fling.Extrapolation
flinger fling.Animation flinger fling.Animation
pid pointer.ID pid pointer.ID
grab bool
last int last int
// Leftover scroll. // Leftover scroll.
scroll float32 scroll float32
@@ -163,7 +161,10 @@ const touchSlop = unit.Dp(3)
// Add the handler to the operation list to receive click events. // Add the handler to the operation list to receive click events.
func (c *Click) Add(ops *op.Ops) { func (c *Click) Add(ops *op.Ops) {
event.Op(ops, c) pointer.InputOp{
Tag: c,
Kinds: pointer.Press | pointer.Release | pointer.Enter | pointer.Leave,
}.Add(ops)
} }
// Hovered returns whether a pointer is inside the area. // Hovered returns whether a pointer is inside the area.
@@ -176,16 +177,10 @@ func (c *Click) Pressed() bool {
return c.pressed return c.pressed
} }
// Update state and return the next click events, if any. // Update state and return the click events.
func (c *Click) Update(q input.Source) (ClickEvent, bool) { func (c *Click) Update(q event.Queue) []ClickEvent {
for { var events []ClickEvent
evt, ok := q.Event(pointer.Filter{ for _, evt := range q.Events(c) {
Target: c,
Kinds: pointer.Press | pointer.Release | pointer.Enter | pointer.Leave | pointer.Cancel,
})
if !ok {
break
}
e, ok := evt.(pointer.Event) e, ok := evt.(pointer.Event)
if !ok { if !ok {
continue continue
@@ -197,15 +192,9 @@ func (c *Click) Update(q input.Source) (ClickEvent, bool) {
} }
c.pressed = false c.pressed = false
if !c.entered || c.hovered { if !c.entered || c.hovered {
return ClickEvent{ events = append(events, ClickEvent{Kind: KindClick, Position: e.Position.Round(), Source: e.Source, Modifiers: e.Modifiers, NumClicks: c.clicks})
Kind: KindClick,
Position: e.Position.Round(),
Source: e.Source,
Modifiers: e.Modifiers,
NumClicks: c.clicks,
}, true
} else { } else {
return ClickEvent{Kind: KindCancel}, true events = append(events, ClickEvent{Kind: KindCancel})
} }
case pointer.Cancel: case pointer.Cancel:
wasPressed := c.pressed wasPressed := c.pressed
@@ -213,7 +202,7 @@ func (c *Click) Update(q input.Source) (ClickEvent, bool) {
c.hovered = false c.hovered = false
c.entered = false c.entered = false
if wasPressed { if wasPressed {
return ClickEvent{Kind: KindCancel}, true events = append(events, ClickEvent{Kind: KindCancel})
} }
case pointer.Press: case pointer.Press:
if c.pressed { if c.pressed {
@@ -235,7 +224,7 @@ func (c *Click) Update(q input.Source) (ClickEvent, bool) {
c.clicks = 1 c.clicks = 1
} }
c.clickedAt = e.Time c.clickedAt = e.Time
return ClickEvent{Kind: KindPress, Position: e.Position.Round(), Source: e.Source, Modifiers: e.Modifiers, NumClicks: c.clicks}, true events = append(events, ClickEvent{Kind: KindPress, Position: e.Position.Round(), Source: e.Source, Modifiers: e.Modifiers, NumClicks: c.clicks})
case pointer.Leave: case pointer.Leave:
if !c.pressed { if !c.pressed {
c.pid = e.PointerID c.pid = e.PointerID
@@ -253,16 +242,25 @@ func (c *Click) Update(q input.Source) (ClickEvent, bool) {
} }
} }
} }
return ClickEvent{}, false return events
} }
func (ClickEvent) ImplementsEvent() {} func (ClickEvent) ImplementsEvent() {}
// Add the handler to the operation list to receive scroll events. // Add the handler to the operation list to receive scroll events.
// The bounds variable refers to the scrolling boundaries // The bounds variable refers to the scrolling boundaries
// as defined in [pointer.Filter]. // as defined in io/pointer.InputOp.
func (s *Scroll) Add(ops *op.Ops) { func (s *Scroll) Add(ops *op.Ops, bounds image.Rectangle) {
event.Op(ops, s) oph := pointer.InputOp{
Tag: s,
Grab: s.grab,
Kinds: pointer.Press | pointer.Drag | pointer.Release | pointer.Scroll,
ScrollBounds: bounds,
}
oph.Add(ops)
if s.flinger.Active() {
op.InvalidateOp{}.Add(ops)
}
} }
// Stop any remaining fling movement. // Stop any remaining fling movement.
@@ -271,19 +269,13 @@ func (s *Scroll) Stop() {
} }
// Update state and report the scroll distance along axis. // Update state and report the scroll distance along axis.
func (s *Scroll) Update(cfg unit.Metric, q input.Source, t time.Time, axis Axis, scrollx, scrolly pointer.ScrollRange) int { func (s *Scroll) Update(cfg unit.Metric, q event.Queue, t time.Time, axis Axis) int {
total := 0 if s.axis != axis {
f := pointer.Filter{ s.axis = axis
Target: s, return 0
Kinds: pointer.Press | pointer.Drag | pointer.Release | pointer.Scroll | pointer.Cancel,
ScrollX: scrollx,
ScrollY: scrolly,
} }
for { total := 0
evt, ok := q.Event(f) for _, evt := range q.Events(s) {
if !ok {
break
}
e, ok := evt.(pointer.Event) e, ok := evt.(pointer.Event)
if !ok { if !ok {
continue continue
@@ -300,7 +292,7 @@ func (s *Scroll) Update(cfg unit.Metric, q input.Source, t time.Time, axis Axis,
} }
s.Stop() s.Stop()
s.estimator = fling.Extrapolation{} s.estimator = fling.Extrapolation{}
v := s.val(axis, e.Position) v := s.val(e.Position)
s.last = int(math.Round(float64(v))) s.last = int(math.Round(float64(v)))
s.estimator.Sample(e.Time, v) s.estimator.Sample(e.Time, v)
s.dragging = true s.dragging = true
@@ -316,14 +308,13 @@ func (s *Scroll) Update(cfg unit.Metric, q input.Source, t time.Time, axis Axis,
fallthrough fallthrough
case pointer.Cancel: case pointer.Cancel:
s.dragging = false s.dragging = false
s.grab = false
case pointer.Scroll: case pointer.Scroll:
switch axis { switch s.axis {
case Horizontal: case Horizontal:
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)
@@ -332,14 +323,14 @@ func (s *Scroll) Update(cfg unit.Metric, q input.Source, t time.Time, axis Axis,
if !s.dragging || s.pid != e.PointerID { if !s.dragging || s.pid != e.PointerID {
continue continue
} }
val := s.val(axis, e.Position) val := s.val(e.Position)
s.estimator.Sample(e.Time, val) s.estimator.Sample(e.Time, val)
v := int(math.Round(float64(val))) v := int(math.Round(float64(val)))
dist := s.last - v dist := s.last - v
if e.Priority < pointer.Grabbed { if e.Priority < pointer.Grabbed {
slop := cfg.Dp(touchSlop) slop := cfg.Dp(touchSlop)
if dist := dist; dist >= slop || -slop >= dist { if dist := dist; dist >= slop || -slop >= dist {
q.Execute(pointer.GrabCmd{Tag: s, ID: e.PointerID}) s.grab = true
} }
} else { } else {
s.last = v s.last = v
@@ -348,22 +339,14 @@ func (s *Scroll) Update(cfg unit.Metric, q input.Source, t time.Time, axis Axis,
} }
} }
total += s.flinger.Tick(t) total += s.flinger.Tick(t)
if s.flinger.Active() {
q.Execute(op.InvalidateCmd{})
}
return total return total
} }
func (s *Scroll) val(axis Axis, p f32.Point) float32 { func (s *Scroll) val(p f32.Point) float32 {
switch axis { if s.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
} }
} }
@@ -381,20 +364,18 @@ func (s *Scroll) State() ScrollState {
// Add the handler to the operation list to receive drag events. // Add the handler to the operation list to receive drag events.
func (d *Drag) Add(ops *op.Ops) { func (d *Drag) Add(ops *op.Ops) {
event.Op(ops, d) pointer.InputOp{
Tag: d,
Grab: d.grab,
Kinds: pointer.Press | pointer.Drag | pointer.Release,
}.Add(ops)
} }
// Update state and return the next drag event, if any. // Update state and return the drag events.
func (d *Drag) Update(cfg unit.Metric, q input.Source, axis Axis) (pointer.Event, bool) { func (d *Drag) Update(cfg unit.Metric, q event.Queue, axis Axis) []pointer.Event {
for { var events []pointer.Event
ev, ok := q.Event(pointer.Filter{ for _, e := range q.Events(d) {
Target: d, e, ok := e.(pointer.Event)
Kinds: pointer.Press | pointer.Drag | pointer.Release | pointer.Cancel,
})
if !ok {
break
}
e, ok := ev.(pointer.Event)
if !ok { if !ok {
continue continue
} }
@@ -427,7 +408,7 @@ func (d *Drag) Update(cfg unit.Metric, q input.Source, axis Axis) (pointer.Event
diff := e.Position.Sub(d.start) diff := e.Position.Sub(d.start)
slop := cfg.Dp(touchSlop) slop := cfg.Dp(touchSlop)
if diff.X*diff.X+diff.Y*diff.Y > float32(slop*slop) { if diff.X*diff.X+diff.Y*diff.Y > float32(slop*slop) {
q.Execute(pointer.GrabCmd{Tag: d, ID: e.PointerID}) d.grab = true
} }
} }
case pointer.Release, pointer.Cancel: case pointer.Release, pointer.Cancel:
@@ -436,12 +417,13 @@ func (d *Drag) Update(cfg unit.Metric, q input.Source, axis Axis) (pointer.Event
continue continue
} }
d.dragging = false d.dragging = false
d.grab = false
} }
return e, true events = append(events, e)
} }
return pointer.Event{}, false return events
} }
// Dragging reports whether it is currently in use. // Dragging reports whether it is currently in use.
+17 -17
View File
@@ -9,8 +9,8 @@ import (
"gioui.org/f32" "gioui.org/f32"
"gioui.org/io/event" "gioui.org/io/event"
"gioui.org/io/input"
"gioui.org/io/pointer" "gioui.org/io/pointer"
"gioui.org/io/router"
"gioui.org/op" "gioui.org/op"
"gioui.org/op/clip" "gioui.org/op/clip"
) )
@@ -22,21 +22,20 @@ func TestHover(t *testing.T) {
stack := clip.Rect(rect).Push(ops) stack := clip.Rect(rect).Push(ops)
h.Add(ops) h.Add(ops)
stack.Pop() stack.Pop()
r := new(input.Router) r := new(router.Router)
h.Update(r.Source())
r.Frame(ops) r.Frame(ops)
r.Queue( r.Queue(
pointer.Event{Kind: pointer.Move, Position: f32.Pt(30, 30)}, pointer.Event{Kind: pointer.Move, Position: f32.Pt(30, 30)},
) )
if !h.Update(r.Source()) { if !h.Update(r) {
t.Fatal("expected hovered") t.Fatal("expected hovered")
} }
r.Queue( r.Queue(
pointer.Event{Kind: pointer.Move, Position: f32.Pt(50, 50)}, pointer.Event{Kind: pointer.Move, Position: f32.Pt(50, 50)},
) )
if h.Update(r.Source()) { if h.Update(r) {
t.Fatal("expected not hovered") t.Fatal("expected not hovered")
} }
} }
@@ -72,21 +71,12 @@ func TestMouseClicks(t *testing.T) {
var ops op.Ops var ops op.Ops
click.Add(&ops) click.Add(&ops)
var r input.Router var r router.Router
click.Update(r.Source())
r.Frame(&ops) r.Frame(&ops)
r.Queue(tc.events...) r.Queue(tc.events...)
var clicks []ClickEvent events := click.Update(&r)
for { clicks := filterMouseClicks(events)
ev, ok := click.Update(r.Source())
if !ok {
break
}
if ev.Kind == KindClick {
clicks = append(clicks, ev)
}
}
if got, want := len(clicks), len(tc.clicks); got != want { if got, want := len(clicks), len(tc.clicks); got != want {
t.Fatalf("got %d mouse clicks, expected %d", got, want) t.Fatalf("got %d mouse clicks, expected %d", got, want)
} }
@@ -116,3 +106,13 @@ func mouseClickEvents(times ...time.Duration) []event.Event {
} }
return events return events
} }
func filterMouseClicks(events []ClickEvent) []ClickEvent {
var clicks []ClickEvent
for _, ev := range events {
if ev.Kind == KindClick {
clicks = append(clicks, ev)
}
}
return clicks
}
+8 -8
View File
@@ -1,16 +1,16 @@
module gioui.org module gioui.org
go 1.24.0 go 1.19
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.0.0-20230803102845-24e03d8b5372
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.39.0 golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64
golang.org/x/text v0.32.0
) )
require golang.org/x/net v0.48.0 require golang.org/x/text v0.7.0
+38 -16
View File
@@ -1,21 +1,43 @@
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.0.0-20230803102845-24e03d8b5372 h1:FQivqchis6bE2/9uF70M2gmmLpe82esEm2QadL0TEJo=
github.com/go-text/typesetting v0.3.0/go.mod h1:qjZLkhRgOEYMhU9eHBr3AR4sfnGJvOXNLt8yRAySFuY= github.com/go-text/typesetting v0.0.0-20230803102845-24e03d8b5372/go.mod h1:evDBbvNR/KaVFZ2ZlDSOWWXIUKq0wCOEtzLxRM8SG3k=
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066 h1:qCuYC+94v2xrb1PoS4NIDe7DGYtLnU2wWiQe9a1B1c0= github.com/go-text/typesetting-utils v0.0.0-20230616150549-2a7df14b6a22 h1:LBQTFxP2MfsyEDqSKmUBZaDuDHN1vpqDyOZjcqS7MYI=
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8= 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 h1:tMSqXTK+AQdW3LpCbfatHSRPHeW6+2WuxaVQuHftn80= golang.org/x/exp v0.0.0-20221012211006-4de253d81b95 h1:sBdrWpxhGDdTAYNqbgBLAR+ULAPPhfgncLr1X0lyWtg=
golang.org/x/exp/shiny v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:ygj7T6vSGhhm/9yTpOQQNvuAUFziTH7RUiH74EoE2C8= golang.org/x/exp v0.0.0-20221012211006-4de253d81b95/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY= golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91 h1:ryT6Nf0R83ZgD8WnFFdfI8wCeyqgdXWN4+CkFVNPAT0=
golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c= golang.org/x/exp/shiny v0.0.0-20220827204233-334a2380cb91/go.mod h1:VjAR7z0ngyATZTELrBSkxOOHhhlnVUxDye4mcjx5h/8=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= 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.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM=
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/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 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
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=
+10 -15
View File
@@ -8,13 +8,8 @@ import (
"gioui.org/internal/f32" "gioui.org/internal/f32"
) )
type textureCacheKey struct { type resourceCache struct {
filter byte res map[interface{}]resourceCacheValue
handle any
}
type textureCache struct {
res map[textureCacheKey]resourceCacheValue
} }
type resourceCacheValue struct { type resourceCacheValue struct {
@@ -42,13 +37,13 @@ type opCacheValue struct {
keep bool keep bool
} }
func newTextureCache() *textureCache { func newResourceCache() *resourceCache {
return &textureCache{ return &resourceCache{
res: make(map[textureCacheKey]resourceCacheValue), res: make(map[interface{}]resourceCacheValue),
} }
} }
func (r *textureCache) get(key textureCacheKey) (resource, bool) { func (r *resourceCache) get(key interface{}) (resource, bool) {
v, exists := r.res[key] v, exists := r.res[key]
if !exists { if !exists {
return nil, false return nil, false
@@ -60,17 +55,17 @@ func (r *textureCache) get(key textureCacheKey) (resource, bool) {
return v.resource, exists return v.resource, exists
} }
func (r *textureCache) put(key textureCacheKey, val resource) { func (r *resourceCache) put(key interface{}, val resource) {
v, exists := r.res[key] v, exists := r.res[key]
if exists && v.used { if exists && v.used {
panic(fmt.Errorf("key exists, %v", key)) panic(fmt.Errorf("key exists, %p", key))
} }
v.used = true v.used = true
v.resource = val v.resource = val
r.res[key] = v r.res[key] = v
} }
func (r *textureCache) frame() { func (r *resourceCache) frame() {
for k, v := range r.res { for k, v := range r.res {
if v.used { if v.used {
v.used = false v.used = false
@@ -82,7 +77,7 @@ func (r *textureCache) frame() {
} }
} }
func (r *textureCache) release() { func (r *resourceCache) release() {
for _, v := range r.res { for _, v := range r.res {
v.resource.release() v.resource.release()
} }
+24
View File
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: Unlicense OR MIT
package gpu
import "testing"
func BenchmarkResourceCache(b *testing.B) {
offset := 0
const N = 100
cache := newResourceCache()
for i := 0; i < b.N; i++ {
// half are the same and half updated
for k := 0; k < N; k++ {
cache.put(offset+k, nullResource{})
}
cache.frame()
offset += N / 2
}
}
type nullResource struct{}
func (nullResource) release() {}
+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},
+2202
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)
}
+146 -166
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"
@@ -45,10 +44,14 @@ type GPU interface {
Clear(color color.NRGBA) Clear(color color.NRGBA)
// Frame draws the graphics operations from op into a viewport of target. // Frame draws the graphics operations from op into a viewport of target.
Frame(frame *op.Ops, target RenderTarget, viewport image.Point) error Frame(frame *op.Ops, target RenderTarget, viewport image.Point) error
// Profile returns the last available profiling information. Profiling
// information is requested when Frame sees an io/profile.Op, and the result
// is available through Profile at some later time.
Profile() string
} }
type gpu struct { type gpu struct {
cache *textureCache cache *resourceCache
profile string profile string
timers *timers timers *timers
@@ -70,6 +73,7 @@ type renderer struct {
} }
type drawOps struct { type drawOps struct {
profile bool
reader ops.Reader reader ops.Reader
states []f32.Affine2D states []f32.Affine2D
transStack []f32.Affine2D transStack []f32.Affine2D
@@ -190,7 +194,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 +205,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{}
@@ -262,7 +266,7 @@ type texture struct {
type blitter struct { type blitter struct {
ctx driver.Device ctx driver.Device
viewport image.Point viewport image.Point
pipelines [2][3]*pipeline pipelines [3]*pipeline
colUniforms *blitColUniforms colUniforms *blitColUniforms
texUniforms *blitTexUniforms texUniforms *blitTexUniforms
linearGradientUniforms *blitLinearGradientUniforms linearGradientUniforms *blitLinearGradientUniforms
@@ -344,17 +348,18 @@ 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) {
g := &gpu{ g := &gpu{
cache: newTextureCache(), cache: newResourceCache(),
} }
g.drawOps.pathCache = newOpCache() g.drawOps.pathCache = newOpCache()
if err := g.init(ctx); err != nil { if err := g.init(ctx); err != nil {
@@ -394,7 +399,7 @@ func (g *gpu) collect(viewport image.Point, frameOps *op.Ops) {
g.renderer.pather.viewport = viewport g.renderer.pather.viewport = viewport
g.drawOps.reset(viewport) g.drawOps.reset(viewport)
g.drawOps.collect(frameOps, viewport) g.drawOps.collect(frameOps, viewport)
if false && g.timers == nil && g.ctx.Caps().Features.Has(driver.FeatureTimers) { if g.drawOps.profile && g.timers == nil && g.ctx.Caps().Features.Has(driver.FeatureTimers) {
g.frameStart = time.Now() g.frameStart = time.Now()
g.timers = newTimers(g.ctx) g.timers = newTimers(g.ctx)
g.stencilTimer = g.timers.newTimer() g.stencilTimer = g.timers.newTimer()
@@ -420,9 +425,9 @@ func (g *gpu) frame(target RenderTarget) error {
g.stencilTimer.end() g.stencilTimer.end()
g.coverTimer.begin() g.coverTimer.begin()
g.renderer.uploadImages(g.cache, g.drawOps.imageOps) g.renderer.uploadImages(g.cache, g.drawOps.imageOps)
g.renderer.prepareDrawOps(g.drawOps.imageOps) g.renderer.prepareDrawOps(g.cache, g.drawOps.imageOps)
g.drawOps.layers = g.renderer.packLayers(g.drawOps.layers) g.drawOps.layers = g.renderer.packLayers(g.drawOps.layers)
g.renderer.drawLayers(g.drawOps.layers, g.drawOps.imageOps) g.renderer.drawLayers(g.cache, g.drawOps.layers, g.drawOps.imageOps)
d := driver.LoadDesc{ d := driver.LoadDesc{
ClearColor: g.drawOps.clearColor, ClearColor: g.drawOps.clearColor,
} }
@@ -432,14 +437,14 @@ func (g *gpu) frame(target RenderTarget) error {
} }
g.ctx.BeginRenderPass(defFBO, d) g.ctx.BeginRenderPass(defFBO, d)
g.ctx.Viewport(0, 0, viewport.X, viewport.Y) g.ctx.Viewport(0, 0, viewport.X, viewport.Y)
g.renderer.drawOps(false, image.Point{}, g.renderer.blitter.viewport, g.drawOps.imageOps) g.renderer.drawOps(g.cache, false, image.Point{}, g.renderer.blitter.viewport, g.drawOps.imageOps)
g.coverTimer.end() g.coverTimer.end()
g.ctx.EndRenderPass() g.ctx.EndRenderPass()
g.cleanupTimer.begin() g.cleanupTimer.begin()
g.cache.frame() g.cache.frame()
g.drawOps.pathCache.frame() g.drawOps.pathCache.frame()
g.cleanupTimer.end() g.cleanupTimer.end()
if false && g.timers.ready() { if g.drawOps.profile && g.timers.ready() {
st, covt, cleant := g.stencilTimer.Elapsed, g.coverTimer.Elapsed, g.cleanupTimer.Elapsed st, covt, cleant := g.stencilTimer.Elapsed, g.coverTimer.Elapsed, g.cleanupTimer.Elapsed
ft := st + covt + cleant ft := st + covt + cleant
q := 100 * time.Microsecond q := 100 * time.Microsecond
@@ -455,8 +460,12 @@ func (g *gpu) Profile() string {
return g.profile return g.profile
} }
func (r *renderer) texHandle(cache *textureCache, data imageOpData) driver.Texture { func (r *renderer) texHandle(cache *resourceCache, data imageOpData) driver.Texture {
key := textureCacheKey{ type cachekey struct {
filter byte
handle any
}
key := cachekey{
filter: data.filter, filter: data.filter,
handle: data.handle, handle: data.handle,
} }
@@ -548,7 +557,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)
@@ -560,24 +569,12 @@ func newBlitter(ctx driver.Device) *blitter {
func (b *blitter) release() { func (b *blitter) release() {
b.quadVerts.Release() b.quadVerts.Release()
for _, p := range b.pipelines { for _, p := range b.pipelines {
for _, p := range p { p.Release()
p.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{}) ([3]*pipeline, error) {
defer func() { var pipelines [3]*pipeline
if err != nil {
for _, p := range pipelines {
for _, p := range p {
if p != nil {
p.Release()
}
}
}
}
}()
blend := driver.BlendDesc{ blend := driver.BlendDesc{
Enable: true, Enable: true,
SrcFactor: driver.BlendFactorOne, SrcFactor: driver.BlendFactorOne,
@@ -595,76 +592,86 @@ func createColorPrograms(b driver.Device, vsSrc shader.Sources, fsSrc [3]shader.
return pipelines, err return pipelines, err
} }
defer vsh.Release() defer vsh.Release()
for i, format := range []driver.TextureFormat{driver.TextureFormatOutput, driver.TextureFormatSRGBA} { {
{ fsh, err := b.NewFragmentShader(fsSrc[materialTexture])
fsh, err := b.NewFragmentShader(fsSrc[materialTexture]) if err != nil {
if err != nil { return pipelines, err
return pipelines, err
}
defer fsh.Release()
pipe, err := b.NewPipeline(driver.PipelineDesc{
VertexShader: vsh,
FragmentShader: fsh,
BlendDesc: blend,
VertexLayout: layout,
PixelFormat: format,
Topology: driver.TopologyTriangleStrip,
})
if err != nil {
return pipelines, err
}
var vertBuffer *uniformBuffer
if u := uniforms[materialTexture]; u != nil {
vertBuffer = newUniformBuffer(b, u)
}
pipelines[i][materialTexture] = &pipeline{pipe, vertBuffer}
} }
{ defer fsh.Release()
var vertBuffer *uniformBuffer pipe, err := b.NewPipeline(driver.PipelineDesc{
fsh, err := b.NewFragmentShader(fsSrc[materialColor]) VertexShader: vsh,
if err != nil { FragmentShader: fsh,
return pipelines, err BlendDesc: blend,
} VertexLayout: layout,
defer fsh.Release() PixelFormat: driver.TextureFormatOutput,
pipe, err := b.NewPipeline(driver.PipelineDesc{ Topology: driver.TopologyTriangleStrip,
VertexShader: vsh, })
FragmentShader: fsh, if err != nil {
BlendDesc: blend, return pipelines, err
VertexLayout: layout,
PixelFormat: format,
Topology: driver.TopologyTriangleStrip,
})
if err != nil {
return pipelines, err
}
if u := uniforms[materialColor]; u != nil {
vertBuffer = newUniformBuffer(b, u)
}
pipelines[i][materialColor] = &pipeline{pipe, vertBuffer}
} }
{ var vertBuffer *uniformBuffer
var vertBuffer *uniformBuffer if u := uniforms[materialTexture]; u != nil {
fsh, err := b.NewFragmentShader(fsSrc[materialLinearGradient]) vertBuffer = newUniformBuffer(b, u)
if err != nil {
return pipelines, err
}
defer fsh.Release()
pipe, err := b.NewPipeline(driver.PipelineDesc{
VertexShader: vsh,
FragmentShader: fsh,
BlendDesc: blend,
VertexLayout: layout,
PixelFormat: format,
Topology: driver.TopologyTriangleStrip,
})
if err != nil {
return pipelines, err
}
if u := uniforms[materialLinearGradient]; u != nil {
vertBuffer = newUniformBuffer(b, u)
}
pipelines[i][materialLinearGradient] = &pipeline{pipe, vertBuffer}
} }
pipelines[materialTexture] = &pipeline{pipe, vertBuffer}
}
{
var vertBuffer *uniformBuffer
fsh, err := b.NewFragmentShader(fsSrc[materialColor])
if err != nil {
pipelines[materialTexture].Release()
return pipelines, err
}
defer fsh.Release()
pipe, err := b.NewPipeline(driver.PipelineDesc{
VertexShader: vsh,
FragmentShader: fsh,
BlendDesc: blend,
VertexLayout: layout,
PixelFormat: driver.TextureFormatOutput,
Topology: driver.TopologyTriangleStrip,
})
if err != nil {
pipelines[materialTexture].Release()
return pipelines, err
}
if u := uniforms[materialColor]; u != nil {
vertBuffer = newUniformBuffer(b, u)
}
pipelines[materialColor] = &pipeline{pipe, vertBuffer}
}
{
var vertBuffer *uniformBuffer
fsh, err := b.NewFragmentShader(fsSrc[materialLinearGradient])
if err != nil {
pipelines[materialTexture].Release()
pipelines[materialColor].Release()
return pipelines, err
}
defer fsh.Release()
pipe, err := b.NewPipeline(driver.PipelineDesc{
VertexShader: vsh,
FragmentShader: fsh,
BlendDesc: blend,
VertexLayout: layout,
PixelFormat: driver.TextureFormatOutput,
Topology: driver.TopologyTriangleStrip,
})
if err != nil {
pipelines[materialTexture].Release()
pipelines[materialColor].Release()
return pipelines, err
}
if u := uniforms[materialLinearGradient]; u != nil {
vertBuffer = newUniformBuffer(b, u)
}
pipelines[materialLinearGradient] = &pipeline{pipe, vertBuffer}
}
if err != nil {
for _, p := range pipelines {
p.Release()
}
return pipelines, err
} }
return pipelines, nil return pipelines, nil
} }
@@ -823,7 +830,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.
@@ -846,7 +853,7 @@ func (r *renderer) packLayers(layers []opacityLayer) []opacityLayer {
return layers return layers
} }
func (r *renderer) drawLayers(layers []opacityLayer, ops []imageOp) { func (r *renderer) drawLayers(cache *resourceCache, layers []opacityLayer, ops []imageOp) {
if len(r.layers.sizes) == 0 { if len(r.layers.sizes) == 0 {
return return
} }
@@ -867,12 +874,12 @@ func (r *renderer) drawLayers(layers []opacityLayer, ops []imageOp) {
Min: l.place.Pos, Min: l.place.Pos,
Max: l.place.Pos.Add(l.clip.Size()), Max: l.place.Pos.Add(l.clip.Size()),
} }
r.ctx.Viewport(v.Min.X, v.Min.Y, v.Dx(), v.Dy()) r.ctx.Viewport(v.Min.X, v.Min.Y, v.Max.X, v.Max.Y)
f := r.layerFBOs.fbos[fbo] f := r.layerFBOs.fbos[fbo]
r.drawOps(true, l.clip.Min.Mul(-1), l.clip.Size(), ops[l.opStart:l.opEnd]) r.drawOps(cache, 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,
@@ -892,6 +899,7 @@ func (r *renderer) drawLayers(layers []opacityLayer, ops []imageOp) {
} }
func (d *drawOps) reset(viewport image.Point) { func (d *drawOps) reset(viewport image.Point) {
d.profile = false
d.viewport = viewport d.viewport = viewport
d.imageOps = d.imageOps[:0] d.imageOps = d.imageOps[:0]
d.pathOps = d.pathOps[:0] d.pathOps = d.pathOps[:0]
@@ -932,7 +940,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 f32.Rectangle, off f32.Point) { func (d *drawOps) addClipPath(state *drawState, aux []byte, auxKey opKey, bounds f32.Rectangle, off f32.Point, push bool) {
npath := d.newPathOp() npath := d.newPathOp()
*npath = pathOp{ *npath = pathOp{
parent: state.cpath, parent: state.cpath,
@@ -957,9 +965,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
} }
@@ -974,13 +980,12 @@ func (k opKey) SetTransform(t f32.Affine2D) opKey {
} }
func (d *drawOps) collectOps(r *ops.Reader, viewport f32.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},
} }
} }
@@ -988,6 +993,8 @@ func (d *drawOps) collectOps(r *ops.Reader, viewport f32.Rectangle) {
loop: loop:
for encOp, ok := r.Decode(); ok; encOp, ok = r.Decode() { for encOp, ok := r.Decode(); ok; encOp, ok = r.Decode() {
switch ops.OpType(encOp.Data[0]) { switch ops.OpType(encOp.Data[0]) {
case ops.TypeProfile:
d.profile = true
case ops.TypeTransform: case ops.TypeTransform:
dop, push := ops.DecodeTransform(encOp.Data) dop, push := ops.DecodeTransform(encOp.Data)
if push { if push {
@@ -1036,7 +1043,7 @@ loop:
op.Decode(encOp.Data) op.Decode(encOp.Data)
quads.key.outline = op.Outline quads.key.outline = op.Outline
bounds := f32.FRect(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
@@ -1059,9 +1066,8 @@ 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, true)
quads = quadsOp{} quads = quadsOp{}
case ops.TypePopClip: case ops.TypePopClip:
state.cpath = state.cpath.parent state.cpath = state.cpath.parent
@@ -1083,7 +1089,7 @@ 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 := float32(1e6) inf := float32(1e6)
@@ -1105,8 +1111,8 @@ 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, false)
} }
bounds := cl.Round() bounds := cl.Round()
@@ -1166,7 +1172,6 @@ func expandPathOp(p *pathOp, clip image.Rectangle) {
func (d *drawState) materialFor(rect f32.Rectangle, off f32.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:
@@ -1200,13 +1205,13 @@ func (d *drawState) materialFor(rect f32.Rectangle, off f32.Point, partTrans f32
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
} }
func (r *renderer) uploadImages(cache *textureCache, ops []imageOp) { func (r *renderer) uploadImages(cache *resourceCache, ops []imageOp) {
for i := range ops { for i := range ops {
img := &ops[i] img := &ops[i]
m := img.material m := img.material
@@ -1216,7 +1221,7 @@ func (r *renderer) uploadImages(cache *textureCache, ops []imageOp) {
} }
} }
func (r *renderer) prepareDrawOps(ops []imageOp) { func (r *renderer) prepareDrawOps(cache *resourceCache, ops []imageOp) {
for _, img := range ops { for _, img := range ops {
m := img.material m := img.material
switch m.material { switch m.material {
@@ -1237,7 +1242,7 @@ func (r *renderer) prepareDrawOps(ops []imageOp) {
} }
} }
func (r *renderer) drawOps(isFBO bool, opOff, viewport image.Point, ops []imageOp) { func (r *renderer) drawOps(cache *resourceCache, isFBO bool, opOff image.Point, viewport image.Point, ops []imageOp) {
var coverTex driver.Texture var coverTex driver.Texture
for i := 0; i < len(ops); i++ { for i := 0; i < len(ops); i++ {
img := ops[i] img := ops[i]
@@ -1251,13 +1256,9 @@ func (r *renderer) drawOps(isFBO bool, opOff, viewport image.Point, ops []imageO
scale, off := clipSpaceTransform(drc, viewport) scale, off := clipSpaceTransform(drc, viewport)
var fbo FBO var fbo FBO
fboIdx := 0
if isFBO {
fboIdx = 1
}
switch img.clipType { switch img.clipType {
case clipTypeNone: case clipTypeNone:
p := r.blitter.pipelines[fboIdx][m.material] p := r.blitter.pipelines[m.material]
r.ctx.BindPipeline(p.pipeline) r.ctx.BindPipeline(p.pipeline)
r.ctx.BindVertexBuffer(r.blitter.quadVerts, 0) r.ctx.BindVertexBuffer(r.blitter.quadVerts, 0)
r.blitter.blit(m.material, isFBO, m.color, m.color1, m.color2, scale, off, m.opacity, m.uvTrans) r.blitter.blit(m.material, isFBO, m.color, m.color1, m.color2, scale, off, m.opacity, m.uvTrans)
@@ -1276,7 +1277,7 @@ func (r *renderer) drawOps(isFBO bool, opOff, viewport image.Point, ops []imageO
Max: img.place.Pos.Add(drc.Size()), Max: img.place.Pos.Add(drc.Size()),
} }
coverScale, coverOff := texSpaceTransform(f32.FRect(uv), fbo.size) coverScale, coverOff := texSpaceTransform(f32.FRect(uv), fbo.size)
p := r.pather.coverer.pipelines[fboIdx][m.material] p := r.pather.coverer.pipelines[m.material]
r.ctx.BindPipeline(p.pipeline) r.ctx.BindPipeline(p.pipeline)
r.ctx.BindVertexBuffer(r.blitter.quadVerts, 0) r.ctx.BindVertexBuffer(r.blitter.quadVerts, 0)
r.pather.cover(m.material, isFBO, m.color, m.color1, m.color2, scale, off, m.uvTrans, coverScale, coverOff) r.pather.cover(m.material, isFBO, m.color, m.color1, m.color2, scale, off, m.uvTrans, coverScale, coverOff)
@@ -1284,11 +1285,7 @@ func (r *renderer) drawOps(isFBO bool, opOff, viewport image.Point, ops []imageO
} }
func (b *blitter) blit(mat materialType, fbo bool, col f32color.RGBA, col1, col2 f32color.RGBA, scale, off f32.Point, opacity float32, uvTrans f32.Affine2D) { func (b *blitter) blit(mat materialType, fbo bool, col f32color.RGBA, col1, col2 f32color.RGBA, scale, off f32.Point, opacity float32, uvTrans f32.Affine2D) {
fboIdx := 0 p := b.pipelines[mat]
if fbo {
fboIdx = 1
}
p := b.pipelines[fboIdx][mat]
b.ctx.BindPipeline(p.pipeline) b.ctx.BindPipeline(p.pipeline)
var uniforms *blitUniforms var uniforms *blitUniforms
switch mat { switch mat {
@@ -1321,7 +1318,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()
@@ -1375,7 +1372,7 @@ func gradientSpaceTransform(clip image.Rectangle, off f32.Point, stop1, stop2 f3
// 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(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
@@ -1489,7 +1486,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:
@@ -1525,10 +1522,12 @@ 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 f32.Rectangle, tr f32.Affine2D) (aux []byte, bnd f32.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
} }
@@ -1575,29 +1574,10 @@ func (d *drawOps) boundsForTransformedRect(r f32.Rectangle, tr f32.Affine2D) (au
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, f32.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 := f32.Pt(float32(iox), float32(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)
} }
+1
View File
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: Unlicense OR MIT // SPDX-License-Identifier: Unlicense OR MIT
//go:build linux || freebsd || openbsd //go:build linux || freebsd || openbsd
// +build linux freebsd openbsd
package headless package headless
+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: 1.9 KiB

After

Width:  |  Height:  |  Size: 334 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 435 B

+17 -16
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)
@@ -483,16 +483,17 @@ func TestGapsInPath(t *testing.T) {
func TestOpacity(t *testing.T) { func TestOpacity(t *testing.T) {
run(t, func(ops *op.Ops) { run(t, func(ops *op.Ops) {
opc1 := paint.PushOpacity(ops, .3) opc1 := paint.PushOpacity(ops, .3)
// Fill screen to exercise the glClear optimization. // Fill screen to exercize the glClear optimization.
paint.FillShape(ops, color.NRGBA{R: 255, A: 255}, clip.Rect{Max: image.Pt(1024, 1024)}.Op()) paint.FillShape(ops, color.NRGBA{R: 255, A: 255}, clip.Rect{Max: image.Pt(1024, 1024)}.Op())
opc2 := paint.PushOpacity(ops, .6) opc2 := paint.PushOpacity(ops, .6)
paint.FillShape(ops, color.NRGBA{G: 255, A: 255}, clip.Rect{Min: image.Pt(20, 10), Max: image.Pt(64, 128)}.Op()) paint.FillShape(ops, color.NRGBA{G: 255, A: 255}, clip.Rect{Min: image.Pt(20, 10), Max: image.Pt(64, 128)}.Op())
opc2.Pop() opc2.Pop()
opc1.Pop() opc1.Pop()
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{G: 255, A: 255}, clip.Rect{Min: image.Pt(50+20, 10), Max: image.Pt(50+64, 128)}.Op())
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 -11
View File
@@ -30,7 +30,7 @@ type pather struct {
type coverer struct { type coverer struct {
ctx driver.Device ctx driver.Device
pipelines [2][3]*pipeline pipelines [3]*pipeline
texUniforms *coverTexUniforms texUniforms *coverTexUniforms
colUniforms *coverColUniforms colUniforms *coverColUniforms
linearGradientUniforms *coverLinearGradientUniforms linearGradientUniforms *coverLinearGradientUniforms
@@ -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
@@ -309,9 +309,7 @@ func (p *pather) release() {
func (c *coverer) release() { func (c *coverer) release() {
for _, p := range c.pipelines { for _, p := range c.pipelines {
for _, p := range p { p.Release()
p.Release()
}
} }
} }
@@ -407,11 +405,7 @@ func (c *coverer) cover(mat materialType, isFBO bool, col f32color.RGBA, col1, c
} }
uniforms.transform = [4]float32{scale.X, scale.Y, off.X, off.Y} uniforms.transform = [4]float32{scale.X, scale.Y, off.X, off.Y}
uniforms.uvCoverTransform = [4]float32{coverScale.X, coverScale.Y, coverOff.X, coverOff.Y} uniforms.uvCoverTransform = [4]float32{coverScale.X, coverScale.Y, coverOff.X, coverOff.Y}
fboIdx := 0 c.pipelines[mat].UploadUniforms(c.ctx)
if isFBO {
fboIdx = 1
}
c.pipelines[fboIdx][mat].UploadUniforms(c.ctx)
c.ctx.DrawArrays(0, 4) c.ctx.DrawArrays(0, 4)
} }
+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())
+1 -1
View File
@@ -34,7 +34,7 @@ func Parse() {
} }
print := false print := false
silent := false silent := false
for part := range strings.SplitSeq(val, ",") { for _, part := range strings.Split(val, ",") {
switch part { switch part {
case textSubsystem: case textSubsystem:
Text.Store(true) Text.Store(true)
+13 -6
View File
@@ -9,16 +9,16 @@ import (
"errors" "errors"
"fmt" "fmt"
"runtime" "runtime"
"slices"
"strings" "strings"
"gioui.org/gpu" "gioui.org/gpu"
) )
type Context struct { type Context struct {
disp _EGLDisplay disp _EGLDisplay
eglCtx *eglContext eglCtx *eglContext
eglSurf _EGLSurface eglSurf _EGLSurface
width, height int
} }
type eglContext struct { type eglContext struct {
@@ -121,9 +121,11 @@ func (c *Context) VisualID() int {
return c.eglCtx.visualID return c.eglCtx.visualID
} }
func (c *Context) CreateSurface(win NativeWindowType) error { func (c *Context) CreateSurface(win NativeWindowType, width, height int) error {
eglSurf, err := createSurface(c.disp, c.eglCtx, win) eglSurf, err := createSurface(c.disp, c.eglCtx, win)
c.eglSurf = eglSurf c.eglSurf = eglSurf
c.width = width
c.height = height
return err return err
} }
@@ -155,7 +157,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)
} }
+1 -1
View File
@@ -665,7 +665,7 @@ func (f *Functions) load(forceES bool) error {
case runtime.GOOS == "android": case runtime.GOOS == "android":
libNames = []string{"libGLESv2.so", "libGLESv3.so"} libNames = []string{"libGLESv2.so", "libGLESv3.so"}
default: default:
libNames = []string{"libGLESv2.so.2", "libGLESv2.so.3.0"} libNames = []string{"libGLESv2.so.2"}
} }
for _, lib := range libNames { for _, lib := range libNames {
if h := dlopen(lib); h != nil { if h := dlopen(lib); h != nil {
+91 -306
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,66 +337,52 @@ 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 {
return ""
}
buf := make([]byte, n) buf := make([]byte, n)
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 +390,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 +403,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 +411,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 +489,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 +501,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)
} }
+1
View File
@@ -1,4 +1,5 @@
//go:build !js //go:build !js
// +build !js
package gl package gl
+65 -15
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
@@ -55,19 +55,28 @@ const (
TypePopTransform TypePopTransform
TypePushOpacity TypePushOpacity
TypePopOpacity TypePopOpacity
TypeInvalidate
TypeImage TypeImage
TypePaint TypePaint
TypeColor TypeColor
TypeLinearGradient TypeLinearGradient
TypePass TypePass
TypePopPass TypePopPass
TypeInput TypePointerInput
TypeKeyInputHint TypeClipboardRead
TypeClipboardWrite
TypeSource
TypeTarget
TypeOffer
TypeKeyInput
TypeKeyFocus
TypeKeySoftKeyboard
TypeSave TypeSave
TypeLoad TypeLoad
TypeAux TypeAux
TypeClip TypeClip
TypePopClip TypePopClip
TypeProfile
TypeCursor TypeCursor
TypePath TypePath
TypeStroke TypeStroke
@@ -76,6 +85,8 @@ const (
TypeSemanticClass TypeSemanticClass
TypeSemanticSelected TypeSemanticSelected
TypeSemanticEnabled TypeSemanticEnabled
TypeSnippet
TypeSelection
TypeActionInput TypeActionInput
) )
@@ -137,13 +148,21 @@ const (
TypeLinearGradientLen = 1 + 8*2 + 4*2 TypeLinearGradientLen = 1 + 8*2 + 4*2
TypePassLen = 1 TypePassLen = 1
TypePopPassLen = 1 TypePopPassLen = 1
TypeInputLen = 1 TypePointerInputLen = 1 + 1 + 1*2 + 2*4 + 2*4
TypeKeyInputHintLen = 1 + 1 TypeClipboardReadLen = 1
TypeClipboardWriteLen = 1
TypeSourceLen = 1
TypeTargetLen = 1
TypeOfferLen = 1
TypeKeyInputLen = 1 + 1
TypeKeyFocusLen = 1 + 1
TypeKeySoftKeyboardLen = 1 + 1
TypeSaveLen = 1 + 4 TypeSaveLen = 1 + 4
TypeLoadLen = 1 + 4 TypeLoadLen = 1 + 4
TypeAuxLen = 1 TypeAuxLen = 1
TypeClipLen = 1 + 4*4 + 1 + 1 TypeClipLen = 1 + 4*4 + 1 + 1
TypePopClipLen = 1 TypePopClipLen = 1
TypeProfileLen = 1
TypeCursorLen = 2 TypeCursorLen = 2
TypePathLen = 8 + 1 TypePathLen = 8 + 1
TypeStrokeLen = 1 + 4 TypeStrokeLen = 1 + 4
@@ -152,6 +171,8 @@ const (
TypeSemanticClassLen = 2 TypeSemanticClassLen = 2
TypeSemanticSelectedLen = 2 TypeSemanticSelectedLen = 2
TypeSemanticEnabledLen = 2 TypeSemanticEnabledLen = 2
TypeSnippetLen = 1 + 4 + 4
TypeSelectionLen = 1 + 2*4 + 2*4 + 4 + 4
TypeActionInputLen = 1 + 1 TypeActionInputLen = 1 + 1
) )
@@ -256,7 +277,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 +290,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:]
@@ -404,19 +425,28 @@ var opProps = [0x100]opProp{
TypePopTransform: {Size: TypePopTransformLen, NumRefs: 0}, TypePopTransform: {Size: TypePopTransformLen, NumRefs: 0},
TypePushOpacity: {Size: TypePushOpacityLen, NumRefs: 0}, TypePushOpacity: {Size: TypePushOpacityLen, NumRefs: 0},
TypePopOpacity: {Size: TypePopOpacityLen, NumRefs: 0}, TypePopOpacity: {Size: TypePopOpacityLen, NumRefs: 0},
TypeInvalidate: {Size: TypeRedrawLen, NumRefs: 0},
TypeImage: {Size: TypeImageLen, NumRefs: 2}, TypeImage: {Size: TypeImageLen, NumRefs: 2},
TypePaint: {Size: TypePaintLen, NumRefs: 0}, TypePaint: {Size: TypePaintLen, NumRefs: 0},
TypeColor: {Size: TypeColorLen, NumRefs: 0}, TypeColor: {Size: TypeColorLen, NumRefs: 0},
TypeLinearGradient: {Size: TypeLinearGradientLen, NumRefs: 0}, TypeLinearGradient: {Size: TypeLinearGradientLen, NumRefs: 0},
TypePass: {Size: TypePassLen, NumRefs: 0}, TypePass: {Size: TypePassLen, NumRefs: 0},
TypePopPass: {Size: TypePopPassLen, NumRefs: 0}, TypePopPass: {Size: TypePopPassLen, NumRefs: 0},
TypeInput: {Size: TypeInputLen, NumRefs: 1}, TypePointerInput: {Size: TypePointerInputLen, NumRefs: 1},
TypeKeyInputHint: {Size: TypeKeyInputHintLen, NumRefs: 1}, TypeClipboardRead: {Size: TypeClipboardReadLen, NumRefs: 1},
TypeClipboardWrite: {Size: TypeClipboardWriteLen, NumRefs: 1},
TypeSource: {Size: TypeSourceLen, NumRefs: 2},
TypeTarget: {Size: TypeTargetLen, NumRefs: 2},
TypeOffer: {Size: TypeOfferLen, NumRefs: 3},
TypeKeyInput: {Size: TypeKeyInputLen, NumRefs: 2},
TypeKeyFocus: {Size: TypeKeyFocusLen, NumRefs: 1},
TypeKeySoftKeyboard: {Size: TypeKeySoftKeyboardLen, NumRefs: 0},
TypeSave: {Size: TypeSaveLen, NumRefs: 0}, TypeSave: {Size: TypeSaveLen, NumRefs: 0},
TypeLoad: {Size: TypeLoadLen, NumRefs: 0}, TypeLoad: {Size: TypeLoadLen, NumRefs: 0},
TypeAux: {Size: TypeAuxLen, NumRefs: 0}, TypeAux: {Size: TypeAuxLen, NumRefs: 0},
TypeClip: {Size: TypeClipLen, NumRefs: 0}, TypeClip: {Size: TypeClipLen, NumRefs: 0},
TypePopClip: {Size: TypePopClipLen, NumRefs: 0}, TypePopClip: {Size: TypePopClipLen, NumRefs: 0},
TypeProfile: {Size: TypeProfileLen, NumRefs: 1},
TypeCursor: {Size: TypeCursorLen, NumRefs: 0}, TypeCursor: {Size: TypeCursorLen, NumRefs: 0},
TypePath: {Size: TypePathLen, NumRefs: 0}, TypePath: {Size: TypePathLen, NumRefs: 0},
TypeStroke: {Size: TypeStrokeLen, NumRefs: 0}, TypeStroke: {Size: TypeStrokeLen, NumRefs: 0},
@@ -425,6 +455,8 @@ var opProps = [0x100]opProp{
TypeSemanticClass: {Size: TypeSemanticClassLen, NumRefs: 0}, TypeSemanticClass: {Size: TypeSemanticClassLen, NumRefs: 0},
TypeSemanticSelected: {Size: TypeSemanticSelectedLen, NumRefs: 0}, TypeSemanticSelected: {Size: TypeSemanticSelectedLen, NumRefs: 0},
TypeSemanticEnabled: {Size: TypeSemanticEnabledLen, NumRefs: 0}, TypeSemanticEnabled: {Size: TypeSemanticEnabledLen, NumRefs: 0},
TypeSnippet: {Size: TypeSnippetLen, NumRefs: 2},
TypeSelection: {Size: TypeSelectionLen, NumRefs: 1},
TypeActionInput: {Size: TypeActionInputLen, NumRefs: 0}, TypeActionInput: {Size: TypeActionInputLen, NumRefs: 0},
} }
@@ -457,6 +489,8 @@ func (t OpType) String() string {
return "PushOpacity" return "PushOpacity"
case TypePopOpacity: case TypePopOpacity:
return "PopOpacity" return "PopOpacity"
case TypeInvalidate:
return "Invalidate"
case TypeImage: case TypeImage:
return "Image" return "Image"
case TypePaint: case TypePaint:
@@ -469,10 +503,24 @@ func (t OpType) String() string {
return "Pass" return "Pass"
case TypePopPass: case TypePopPass:
return "PopPass" return "PopPass"
case TypeInput: case TypePointerInput:
return "Input" return "PointerInput"
case TypeKeyInputHint: case TypeClipboardRead:
return "KeyInputHint" return "ClipboardRead"
case TypeClipboardWrite:
return "ClipboardWrite"
case TypeSource:
return "Source"
case TypeTarget:
return "Target"
case TypeOffer:
return "Offer"
case TypeKeyInput:
return "KeyInput"
case TypeKeyFocus:
return "KeyFocus"
case TypeKeySoftKeyboard:
return "KeySoftKeyboard"
case TypeSave: case TypeSave:
return "Save" return "Save"
case TypeLoad: case TypeLoad:
@@ -483,6 +531,8 @@ func (t OpType) String() string {
return "Clip" return "Clip"
case TypePopClip: case TypePopClip:
return "PopClip" return "PopClip"
case TypeProfile:
return "Profile"
case TypeCursor: case TypeCursor:
return "Cursor" return "Cursor"
case TypePath: case TypePath:
+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")
} }
+10 -22
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,10 @@ 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 {
return f32.Point{}
}
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 +437,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 +470,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 +574,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),
+2 -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
@@ -152,11 +47,12 @@ func BenchmarkSplitCubic(b *testing.B) {
} }
for _, s := range scenarios { for _, s := range scenarios {
s := s
b.Run(strconv.Itoa(s.segments), func(b *testing.B) { b.Run(strconv.Itoa(s.segments), func(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"
+24 -11
View File
@@ -3,22 +3,35 @@
package clipboard package clipboard
import ( import (
"io" "gioui.org/internal/ops"
"gioui.org/io/event" "gioui.org/io/event"
"gioui.org/op"
) )
// WriteCmd copies Text to the clipboard. // Event is generated when the clipboard content is requested.
type WriteCmd struct { type Event struct {
Type string Text string
Data io.ReadCloser
} }
// ReadCmd requests the text of the clipboard, delivered to // ReadOp requests the text of the clipboard, delivered to
// the handler through an [io/transfer.DataEvent]. // the current handler through an Event.
type ReadCmd struct { type ReadOp struct {
Tag event.Tag Tag event.Tag
} }
func (WriteCmd) ImplementsCommand() {} // WriteOp copies Text to the clipboard.
func (ReadCmd) ImplementsCommand() {} type WriteOp struct {
Text string
}
func (h ReadOp) Add(o *op.Ops) {
data := ops.Write1(&o.Internal, ops.TypeClipboardReadLen, h.Tag)
data[0] = byte(ops.TypeClipboardRead)
}
func (h WriteOp) Add(o *op.Ops) {
data := ops.Write1String(&o.Internal, ops.TypeClipboardWriteLen, h.Text)
data[0] = byte(ops.TypeClipboardWrite)
}
func (Event) ImplementsEvent() {}

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