diff options
Diffstat (limited to 'vendor/gioui.org/app')
77 files changed, 6549 insertions, 7753 deletions
diff --git a/vendor/gioui.org/app/internal/window/Gio.java b/vendor/gioui.org/app/Gio.java index 33e1a68..33e1a68 100644 --- a/vendor/gioui.org/app/internal/window/Gio.java +++ b/vendor/gioui.org/app/Gio.java diff --git a/vendor/gioui.org/app/internal/window/GioActivity.java b/vendor/gioui.org/app/GioActivity.java index 260d4b6..4565e38 100644 --- a/vendor/gioui.org/app/internal/window/GioActivity.java +++ b/vendor/gioui.org/app/GioActivity.java @@ -3,12 +3,8 @@ package org.gioui; import android.app.Activity; -import android.content.res.Configuration; -import android.os.Build; import android.os.Bundle; -import android.view.View; -import android.view.Window; -import android.view.WindowManager; +import android.content.res.Configuration; public final class GioActivity extends Activity { private GioView view; @@ -16,13 +12,8 @@ public final class GioActivity extends Activity { @Override public void onCreate(Bundle state) { super.onCreate(state); - Window w = getWindow(); - this.view = new GioView(this); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - this.view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); - } - this.view.setLayoutParams(new WindowManager.LayoutParams(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT)); + setContentView(view); } @@ -48,7 +39,7 @@ public final class GioActivity extends Activity { @Override public void onLowMemory() { super.onLowMemory(); - view.lowMemory(); + GioView.onLowMemory(); } @Override public void onBackPressed() { diff --git a/vendor/gioui.org/app/GioView.java b/vendor/gioui.org/app/GioView.java new file mode 100644 index 0000000..14639b0 --- /dev/null +++ b/vendor/gioui.org/app/GioView.java @@ -0,0 +1,480 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package org.gioui; + +import java.lang.Class; +import java.lang.IllegalAccessException; +import java.lang.InstantiationException; +import java.lang.ExceptionInInitializerError; +import java.lang.SecurityException; +import android.app.Activity; +import android.app.Fragment; +import android.app.FragmentManager; +import android.app.FragmentTransaction; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Rect; +import android.os.Build; +import android.os.Bundle; +import android.text.Editable; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.KeyCharacterMap; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.PointerIcon; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.WindowInsets; +import android.view.Surface; +import android.view.SurfaceView; +import android.view.SurfaceHolder; +import android.view.Window; +import android.view.WindowInsetsController; +import android.view.WindowManager; +import android.view.inputmethod.BaseInputConnection; +import android.view.inputmethod.InputConnection; +import android.view.inputmethod.InputMethodManager; +import android.view.inputmethod.EditorInfo; +import android.text.InputType; +import android.view.accessibility.AccessibilityNodeProvider; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; + +import java.io.UnsupportedEncodingException; + +public final class GioView extends SurfaceView { + private static boolean jniLoaded; + + private final SurfaceHolder.Callback surfCallbacks; + private final View.OnFocusChangeListener focusCallback; + private final InputMethodManager imm; + private final float scrollXScale; + private final float scrollYScale; + private int keyboardHint; + private AccessibilityManager accessManager; + + private long nhandle; + + public GioView(Context context) { + this(context, null); + } + + public GioView(Context context, AttributeSet attrs) { + super(context, attrs); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); + } + setLayoutParams(new WindowManager.LayoutParams(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT)); + + // Late initialization of the Go runtime to wait for a valid context. + Gio.init(context.getApplicationContext()); + + // Set background color to transparent to avoid a flickering + // issue on ChromeOS. + setBackgroundColor(Color.argb(0, 0, 0, 0)); + + ViewConfiguration conf = ViewConfiguration.get(context); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + scrollXScale = conf.getScaledHorizontalScrollFactor(); + scrollYScale = conf.getScaledVerticalScrollFactor(); + + // The platform focus highlight is not aware of Gio's widgets. + setDefaultFocusHighlightEnabled(false); + } else { + float listItemHeight = 48; // dp + float px = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + listItemHeight, + getResources().getDisplayMetrics() + ); + scrollXScale = px; + scrollYScale = px; + } + + accessManager = (AccessibilityManager)context.getSystemService(Context.ACCESSIBILITY_SERVICE); + nhandle = onCreateView(this); + imm = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE); + setFocusable(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() { + @Override public void surfaceCreated(SurfaceHolder holder) { + // Ignore; surfaceChanged is guaranteed to be called immediately after this. + } + @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + onSurfaceChanged(nhandle, getHolder().getSurface()); + } + @Override public void surfaceDestroyed(SurfaceHolder holder) { + onSurfaceDestroyed(nhandle); + } + }; + getHolder().addCallback(surfCallbacks); + } + + @Override public boolean onKeyDown(int keyCode, KeyEvent event) { + if (nhandle != 0) { + onKeyEvent(nhandle, keyCode, event.getUnicodeChar(), event.getEventTime()); + } + return false; + } + + @Override public boolean onGenericMotionEvent(MotionEvent event) { + dispatchMotionEvent(event); + return true; + } + + @Override public boolean onTouchEvent(MotionEvent event) { + // Ask for unbuffered events. Flutter and Chrome do it + // so assume it's good for us as well. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + requestUnbufferedDispatch(event); + } + + dispatchMotionEvent(event); + return true; + } + + private void setCursor(int id) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { + return; + } + PointerIcon pointerIcon = PointerIcon.getSystemIcon(getContext(), id); + setPointerIcon(pointerIcon); + } + + private void setOrientation(int id, int fallback) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { + id = fallback; + } + ((Activity) this.getContext()).setRequestedOrientation(id); + } + + private void setFullscreen(boolean enabled) { + int flags = this.getSystemUiVisibility(); + if (enabled) { + flags |= SYSTEM_UI_FLAG_IMMERSIVE_STICKY; + flags |= SYSTEM_UI_FLAG_HIDE_NAVIGATION; + flags |= SYSTEM_UI_FLAG_FULLSCREEN; + flags |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + } else { + flags &= ~SYSTEM_UI_FLAG_IMMERSIVE_STICKY; + flags &= ~SYSTEM_UI_FLAG_HIDE_NAVIGATION; + flags &= ~SYSTEM_UI_FLAG_FULLSCREEN; + flags &= ~SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + } + this.setSystemUiVisibility(flags); + } + + private enum Bar { + NAVIGATION, + STATUS, + } + + private void setBarColor(Bar t, int color, int luminance) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + return; + } + + Window window = ((Activity) this.getContext()).getWindow(); + + int insetsMask; + int viewMask; + + switch (t) { + case STATUS: + insetsMask = WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; + viewMask = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; + window.setStatusBarColor(color); + break; + case NAVIGATION: + insetsMask = WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; + viewMask = View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; + window.setNavigationBarColor(color); + break; + default: + throw new RuntimeException("invalid bar type"); + } + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + return; + } + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + int flags = this.getSystemUiVisibility(); + if (luminance > 128) { + flags |= viewMask; + } else { + flags &= ~viewMask; + } + this.setSystemUiVisibility(flags); + return; + } + + WindowInsetsController insetsController = window.getInsetsController(); + if (insetsController == null) { + return; + } + if (luminance > 128) { + insetsController.setSystemBarsAppearance(insetsMask, insetsMask); + } else { + insetsController.setSystemBarsAppearance(0, insetsMask); + } + } + + private void setStatusColor(int color, int luminance) { + this.setBarColor(Bar.STATUS, color, luminance); + } + + private void setNavigationColor(int color, int luminance) { + this.setBarColor(Bar.NAVIGATION, color, luminance); + } + + @Override protected boolean dispatchHoverEvent(MotionEvent event) { + if (!accessManager.isTouchExplorationEnabled()) { + return super.dispatchHoverEvent(event); + } + switch (event.getAction()) { + case MotionEvent.ACTION_HOVER_ENTER: + // Fall through. + case MotionEvent.ACTION_HOVER_MOVE: + onTouchExploration(nhandle, event.getX(), event.getY()); + break; + case MotionEvent.ACTION_HOVER_EXIT: + onExitTouchExploration(nhandle); + break; + } + return true; + } + + void sendA11yEvent(int eventType, int viewId) { + if (!accessManager.isEnabled()) { + return; + } + AccessibilityEvent event = obtainA11yEvent(eventType, viewId); + getParent().requestSendAccessibilityEvent(this, event); + } + + AccessibilityEvent obtainA11yEvent(int eventType, int viewId) { + AccessibilityEvent event = AccessibilityEvent.obtain(eventType); + event.setPackageName(getContext().getPackageName()); + event.setSource(this, viewId); + return event; + } + + boolean isA11yActive() { + return accessManager.isEnabled(); + } + + void sendA11yChange(int viewId) { + if (!accessManager.isEnabled()) { + return; + } + AccessibilityEvent event = obtainA11yEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED, viewId); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE); + } + getParent().requestSendAccessibilityEvent(this, event); + } + + private void dispatchMotionEvent(MotionEvent event) { + if (nhandle == 0) { + return; + } + for (int j = 0; j < event.getHistorySize(); j++) { + long time = event.getHistoricalEventTime(j); + for (int i = 0; i < event.getPointerCount(); i++) { + onTouchEvent( + nhandle, + event.ACTION_MOVE, + event.getPointerId(i), + event.getToolType(i), + event.getHistoricalX(i, j), + event.getHistoricalY(i, j), + scrollXScale*event.getHistoricalAxisValue(MotionEvent.AXIS_HSCROLL, i, j), + scrollYScale*event.getHistoricalAxisValue(MotionEvent.AXIS_VSCROLL, i, j), + event.getButtonState(), + time); + } + } + int act = event.getActionMasked(); + int idx = event.getActionIndex(); + for (int i = 0; i < event.getPointerCount(); i++) { + int pact = event.ACTION_MOVE; + if (i == idx) { + pact = act; + } + onTouchEvent( + nhandle, + pact, + event.getPointerId(i), + event.getToolType(i), + event.getX(i), event.getY(i), + scrollXScale*event.getAxisValue(MotionEvent.AXIS_HSCROLL, i), + scrollYScale*event.getAxisValue(MotionEvent.AXIS_VSCROLL, i), + event.getButtonState(), + event.getEventTime()); + } + } + + @Override public InputConnection onCreateInputConnection(EditorInfo editor) { + editor.inputType = this.keyboardHint; + editor.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN | EditorInfo.IME_FLAG_NO_EXTRACT_UI; + return new InputConnection(this); + } + + void setInputHint(int hint) { + if (hint == this.keyboardHint) { + return; + } + this.keyboardHint = hint; + imm.restartInput(this); + } + + void showTextInput() { + GioView.this.requestFocus(); + imm.showSoftInput(GioView.this, 0); + } + + void hideTextInput() { + imm.hideSoftInputFromWindow(getWindowToken(), 0); + } + + @Override protected boolean fitSystemWindows(Rect insets) { + if (nhandle != 0) { + onWindowInsets(nhandle, insets.top, insets.right, insets.bottom, insets.left); + } + return true; + } + + @Override protected void onDraw(Canvas canvas) { + if (nhandle != 0) { + onFrameCallback(nhandle); + } + } + + int getDensity() { + return getResources().getDisplayMetrics().densityDpi; + } + + float getFontScale() { + return getResources().getConfiguration().fontScale; + } + + public void start() { + if (nhandle != 0) { + onStartView(nhandle); + } + } + + public void stop() { + if (nhandle != 0) { + onStopView(nhandle); + } + } + + public void destroy() { + if (nhandle != 0) { + onDestroyView(nhandle); + } + } + + protected void unregister() { + setOnFocusChangeListener(null); + getHolder().removeCallback(surfCallbacks); + nhandle = 0; + } + + public void configurationChanged() { + if (nhandle != 0) { + onConfigurationChanged(nhandle); + } + } + + public boolean backPressed() { + if (nhandle == 0) { + return false; + } + return onBack(nhandle); + } + + static private native long onCreateView(GioView view); + static private native void onDestroyView(long handle); + static private native void onStartView(long handle); + static private native void onStopView(long handle); + static private native void onSurfaceDestroyed(long handle); + static private native void onSurfaceChanged(long handle, Surface surface); + static private native void onConfigurationChanged(long handle); + static private native void onWindowInsets(long handle, int top, int right, int bottom, int left); + static public native void onLowMemory(); + static private native void onTouchEvent(long handle, int action, int pointerID, int tool, float x, float y, float scrollX, float scrollY, int buttons, long time); + static private native void onKeyEvent(long handle, int code, int character, long time); + static private native void onFrameCallback(long handle); + static private native boolean onBack(long handle); + static private native void onFocusChange(long handle, boolean focus); + static private native AccessibilityNodeInfo initializeAccessibilityNodeInfo(long handle, int viewId, int screenX, int screenY, AccessibilityNodeInfo info); + static private native void onTouchExploration(long handle, float x, float y); + static private native void onExitTouchExploration(long handle); + static private native void onA11yFocus(long handle, int viewId); + static private native void onClearA11yFocus(long handle, int viewId); + + private static class InputConnection extends BaseInputConnection { + private final Editable editable; + + InputConnection(View view) { + // Passing false enables "dummy mode", where the BaseInputConnection + // attempts to convert IME operations to key events. + super(view, false); + editable = Editable.Factory.getInstance().newEditable(""); + } + + @Override public Editable getEditable() { + return editable; + } + } + + @Override public AccessibilityNodeProvider getAccessibilityNodeProvider() { + return new AccessibilityNodeProvider() { + private final int[] screenOff = new int[2]; + + @Override public AccessibilityNodeInfo createAccessibilityNodeInfo(int viewId) { + AccessibilityNodeInfo info = null; + if (viewId == View.NO_ID) { + info = AccessibilityNodeInfo.obtain(GioView.this); + GioView.this.onInitializeAccessibilityNodeInfo(info); + } else { + info = AccessibilityNodeInfo.obtain(GioView.this, viewId); + info.setPackageName(getContext().getPackageName()); + info.setVisibleToUser(true); + } + GioView.this.getLocationOnScreen(screenOff); + info = GioView.this.initializeAccessibilityNodeInfo(nhandle, viewId, screenOff[0], screenOff[1], info); + return info; + } + + @Override public boolean performAction(int viewId, int action, Bundle arguments) { + if (viewId == View.NO_ID) { + return GioView.this.performAccessibilityAction(action, arguments); + } + switch (action) { + case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: + GioView.this.onA11yFocus(nhandle, viewId); + GioView.this.sendA11yEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED, viewId); + return true; + case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: + GioView.this.onClearA11yFocus(nhandle, viewId); + GioView.this.sendA11yEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED, viewId); + return true; + } + return false; + } + }; + } +} diff --git a/vendor/gioui.org/app/app.go b/vendor/gioui.org/app/app.go index d21224e..e00298a 100644 --- a/vendor/gioui.org/app/app.go +++ b/vendor/gioui.org/app/app.go @@ -5,8 +5,6 @@ package app import ( "os" "strings" - - "gioui.org/app/internal/window" ) // extraArgs contains extra arguments to append to @@ -44,5 +42,5 @@ func DataDir() (string, error) { // require control of the main thread of the program for // running windows. func Main() { - window.Main() + osMain() } diff --git a/vendor/gioui.org/app/app_android.go b/vendor/gioui.org/app/app_android.go deleted file mode 100644 index 58e40d0..0000000 --- a/vendor/gioui.org/app/app_android.go +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package app - -import ( - "gioui.org/app/internal/window" -) - -// JavaVM returns the global JNI JavaVM. -func JavaVM() uintptr { - return window.JavaVM() -} - -// AppContext returns the global Application context as a JNI -// jobject. -func AppContext() uintptr { - return window.AppContext() -} - -// Do invokes the function with a JNI jobject handle to the underlying -// Android View. The function is invoked on the main thread, and the -// handle is invalidated after the function returns. -// -// Note: Do may deadlock if called from the same goroutine that receives from -// Events. -func (w *Window) Do(f func(view uintptr)) { - type androidDriver interface { - Do(f func(view uintptr)) bool - } - success := make(chan bool) - for { - driver := make(chan androidDriver, 1) - // two-stage process: first wait for a valid driver... - w.driverDo(func() { - driver <- w.driver.(androidDriver) - }) - // .. then run the function on the main thread using the - // driver. The driver Do method returns false if the - // view was invalidated while switching to the main thread. - window.RunOnMain(func() { - d := <-driver - success <- d.Do(f) - }) - if <-success { - break - } - } -} diff --git a/vendor/gioui.org/app/d3d11_windows.go b/vendor/gioui.org/app/d3d11_windows.go new file mode 100644 index 0000000..ab04ee4 --- /dev/null +++ b/vendor/gioui.org/app/d3d11_windows.go @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package app + +import ( + "fmt" + "unsafe" + + "gioui.org/gpu" + "gioui.org/internal/d3d11" +) + +type d3d11Context struct { + win *window + dev *d3d11.Device + ctx *d3d11.DeviceContext + + swchain *d3d11.IDXGISwapChain + renderTarget *d3d11.RenderTargetView + width, height int +} + +const debug = false + +func init() { + drivers = append(drivers, gpuAPI{ + priority: 1, + initializer: func(w *window) (context, error) { + hwnd, _, _ := w.HWND() + var flags uint32 + if debug { + flags |= d3d11.CREATE_DEVICE_DEBUG + } + dev, ctx, _, err := d3d11.CreateDevice( + d3d11.DRIVER_TYPE_HARDWARE, + flags, + ) + if err != nil { + return nil, fmt.Errorf("NewContext: %v", err) + } + swchain, err := d3d11.CreateSwapChain(dev, hwnd) + if err != nil { + d3d11.IUnknownRelease(unsafe.Pointer(ctx), ctx.Vtbl.Release) + d3d11.IUnknownRelease(unsafe.Pointer(dev), dev.Vtbl.Release) + return nil, err + } + return &d3d11Context{win: w, dev: dev, ctx: ctx, swchain: swchain}, nil + }, + }) +} + +func (c *d3d11Context) API() gpu.API { + return gpu.Direct3D11{Device: unsafe.Pointer(c.dev)} +} + +func (c *d3d11Context) RenderTarget() (gpu.RenderTarget, error) { + return gpu.Direct3D11RenderTarget{ + RenderTarget: unsafe.Pointer(c.renderTarget), + }, nil +} + +func (c *d3d11Context) Present() error { + err := c.swchain.Present(1, 0) + if err == nil { + return nil + } + if err, ok := err.(d3d11.ErrorCode); ok { + switch err.Code { + case d3d11.DXGI_STATUS_OCCLUDED: + // Ignore + return nil + case d3d11.DXGI_ERROR_DEVICE_RESET, d3d11.DXGI_ERROR_DEVICE_REMOVED, d3d11.D3DDDIERR_DEVICEREMOVED: + return gpu.ErrDeviceLost + } + } + return err +} + +func (c *d3d11Context) Refresh() error { + var width, height int + _, width, height = c.win.HWND() + if c.renderTarget != nil && width == c.width && height == c.height { + return nil + } + c.releaseFBO() + if err := c.swchain.ResizeBuffers(0, 0, 0, d3d11.DXGI_FORMAT_UNKNOWN, 0); err != nil { + return err + } + c.width = width + c.height = height + + backBuffer, err := c.swchain.GetBuffer(0, &d3d11.IID_Texture2D) + if err != nil { + return err + } + texture := (*d3d11.Resource)(unsafe.Pointer(backBuffer)) + renderTarget, err := c.dev.CreateRenderTargetView(texture) + d3d11.IUnknownRelease(unsafe.Pointer(backBuffer), backBuffer.Vtbl.Release) + if err != nil { + return err + } + c.renderTarget = renderTarget + return nil +} + +func (c *d3d11Context) Lock() error { + c.ctx.OMSetRenderTargets(c.renderTarget, nil) + return nil +} + +func (c *d3d11Context) Unlock() {} + +func (c *d3d11Context) Release() { + c.releaseFBO() + if c.swchain != nil { + d3d11.IUnknownRelease(unsafe.Pointer(c.swchain), c.swchain.Vtbl.Release) + } + if c.ctx != nil { + d3d11.IUnknownRelease(unsafe.Pointer(c.ctx), c.ctx.Vtbl.Release) + } + if c.dev != nil { + d3d11.IUnknownRelease(unsafe.Pointer(c.dev), c.dev.Vtbl.Release) + } + *c = d3d11Context{} + if debug { + d3d11.ReportLiveObjects() + } +} + +func (c *d3d11Context) releaseFBO() { + if c.renderTarget != nil { + d3d11.IUnknownRelease(unsafe.Pointer(c.renderTarget), c.renderTarget.Vtbl.Release) + c.renderTarget = nil + } +} diff --git a/vendor/gioui.org/app/datadir.go b/vendor/gioui.org/app/datadir.go index 31e5453..500a59a 100644 --- a/vendor/gioui.org/app/datadir.go +++ b/vendor/gioui.org/app/datadir.go @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Unlicense OR MIT +//go:build !android // +build !android package app diff --git a/vendor/gioui.org/app/datadir_android.go b/vendor/gioui.org/app/datadir_android.go deleted file mode 100644 index 450e6cf..0000000 --- a/vendor/gioui.org/app/datadir_android.go +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -// +build android - -package app - -import "C" - -import ( - "os" - "path/filepath" - "sync" - - "gioui.org/app/internal/window" -) - -var ( - dataDirOnce sync.Once - dataPath string -) - -func dataDir() (string, error) { - dataDirOnce.Do(func() { - dataPath = window.GetDataDir() - // Set XDG_CACHE_HOME to make os.UserCacheDir work. - if _, exists := os.LookupEnv("XDG_CACHE_HOME"); !exists { - cachePath := filepath.Join(dataPath, "cache") - os.Setenv("XDG_CACHE_HOME", cachePath) - } - // Set XDG_CONFIG_HOME to make os.UserConfigDir work. - if _, exists := os.LookupEnv("XDG_CONFIG_HOME"); !exists { - cfgPath := filepath.Join(dataPath, "config") - os.Setenv("XDG_CONFIG_HOME", cfgPath) - } - // Set HOME to make os.UserHomeDir work. - if _, exists := os.LookupEnv("HOME"); !exists { - os.Setenv("HOME", dataPath) - } - }) - return dataPath, nil -} diff --git a/vendor/gioui.org/app/egl_android.go b/vendor/gioui.org/app/egl_android.go new file mode 100644 index 0000000..2c31130 --- /dev/null +++ b/vendor/gioui.org/app/egl_android.go @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package app + +/* +#include <android/native_window_jni.h> +#include <EGL/egl.h> +*/ +import "C" + +import ( + "unsafe" + + "gioui.org/internal/egl" +) + +type androidContext struct { + win *window + eglSurf egl.NativeWindowType + width, height int + *egl.Context +} + +func init() { + newAndroidGLESContext = func(w *window) (context, error) { + ctx, err := egl.NewContext(nil) + if err != nil { + return nil, err + } + return &androidContext{win: w, Context: ctx}, nil + } +} + +func (c *androidContext) Release() { + if c.Context != nil { + c.Context.Release() + c.Context = nil + } +} + +func (c *androidContext) Refresh() error { + c.Context.ReleaseSurface() + if err := c.win.setVisual(c.Context.VisualID()); err != nil { + return err + } + win, width, height := c.win.nativeWindow() + c.eglSurf = egl.NativeWindowType(unsafe.Pointer(win)) + c.width, c.height = width, height + return nil +} + +func (c *androidContext) Lock() error { + // The Android emulator creates a broken surface if it is not + // created on the same thread as the context is made current. + if c.eglSurf != nil { + if err := c.Context.CreateSurface(c.eglSurf, c.width, c.height); err != nil { + return err + } + c.eglSurf = nil + } + return c.Context.MakeCurrent() +} + +func (c *androidContext) Unlock() { + c.Context.ReleaseCurrent() +} diff --git a/vendor/gioui.org/app/internal/window/egl_wayland.go b/vendor/gioui.org/app/egl_wayland.go index b4878b4..c2406a0 100644 --- a/vendor/gioui.org/app/internal/window/egl_wayland.go +++ b/vendor/gioui.org/app/egl_wayland.go @@ -1,20 +1,22 @@ // SPDX-License-Identifier: Unlicense OR MIT -// +build linux,!android,!nowayland freebsd +//go:build ((linux && !android) || freebsd) && !nowayland +// +build linux,!android freebsd +// +build !nowayland -package window +package app import ( "errors" "unsafe" - "gioui.org/app/internal/egl" + "gioui.org/internal/egl" ) /* #cgo linux pkg-config: egl wayland-egl #cgo freebsd openbsd LDFLAGS: -lwayland-egl -#cgo CFLAGS: -DMESA_EGL_NO_X11_HEADERS +#cgo CFLAGS: -DEGL_NO_X11 #include <EGL/egl.h> #include <wayland-client.h> @@ -22,22 +24,24 @@ import ( */ import "C" -type context struct { +type wlContext struct { win *window *egl.Context eglWin *C.struct_wl_egl_window } -func (w *window) NewContext() (Context, error) { - disp := egl.NativeDisplayType(unsafe.Pointer(w.display())) - ctx, err := egl.NewContext(disp) - if err != nil { - return nil, err +func init() { + newWaylandEGLContext = func(w *window) (context, error) { + disp := egl.NativeDisplayType(unsafe.Pointer(w.display())) + ctx, err := egl.NewContext(disp) + if err != nil { + return nil, err + } + return &wlContext{Context: ctx, win: w}, nil } - return &context{Context: ctx, win: w}, nil } -func (c *context) Release() { +func (c *wlContext) Release() { if c.Context != nil { c.Context.Release() c.Context = nil @@ -48,7 +52,7 @@ func (c *context) Release() { } } -func (c *context) MakeCurrent() error { +func (c *wlContext) Refresh() error { c.Context.ReleaseSurface() if c.eglWin != nil { C.wl_egl_window_destroy(c.eglWin) @@ -64,12 +68,13 @@ func (c *context) MakeCurrent() error { } c.eglWin = eglWin eglSurf := egl.NativeWindowType(uintptr(unsafe.Pointer(eglWin))) - if err := c.Context.CreateSurface(eglSurf, width, height); err != nil { - return err - } - return c.Context.MakeCurrent() + return c.Context.CreateSurface(eglSurf, width, height) } -func (c *context) Lock() {} +func (c *wlContext) Lock() error { + return c.Context.MakeCurrent() +} -func (c *context) Unlock() {} +func (c *wlContext) Unlock() { + c.Context.ReleaseCurrent() +} diff --git a/vendor/gioui.org/app/internal/window/egl_windows.go b/vendor/gioui.org/app/egl_windows.go index 654d279..3a95450 100644 --- a/vendor/gioui.org/app/internal/window/egl_windows.go +++ b/vendor/gioui.org/app/egl_windows.go @@ -1,9 +1,11 @@ // SPDX-License-Identifier: Unlicense OR MIT -package window +package app import ( - "gioui.org/app/internal/egl" + "golang.org/x/sys/windows" + + "gioui.org/internal/egl" ) type glContext struct { @@ -12,9 +14,9 @@ type glContext struct { } func init() { - backends = append(backends, gpuAPI{ + drivers = append(drivers, gpuAPI{ priority: 2, - initializer: func(w *window) (Context, error) { + initializer: func(w *window) (context, error) { disp := egl.NativeDisplayType(w.HDC()) ctx, err := egl.NewContext(disp) if err != nil { @@ -32,9 +34,13 @@ func (c *glContext) Release() { } } -func (c *glContext) MakeCurrent() error { +func (c *glContext) Refresh() error { c.Context.ReleaseSurface() - win, width, height := c.win.HWND() + 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 @@ -43,9 +49,14 @@ func (c *glContext) MakeCurrent() error { return err } c.Context.EnableVSync(true) + c.Context.ReleaseCurrent() return nil } -func (c *glContext) Lock() {} +func (c *glContext) Lock() error { + return c.Context.MakeCurrent() +} -func (c *glContext) Unlock() {} +func (c *glContext) Unlock() { + c.Context.ReleaseCurrent() +} diff --git a/vendor/gioui.org/app/egl_x11.go b/vendor/gioui.org/app/egl_x11.go new file mode 100644 index 0000000..7234808 --- /dev/null +++ b/vendor/gioui.org/app/egl_x11.go @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build ((linux && !android) || freebsd || openbsd) && !nox11 +// +build linux,!android freebsd openbsd +// +build !nox11 + +package app + +import ( + "unsafe" + + "gioui.org/internal/egl" +) + +type x11Context struct { + win *x11Window + *egl.Context +} + +func init() { + newX11EGLContext = func(w *x11Window) (context, error) { + disp := egl.NativeDisplayType(unsafe.Pointer(w.display())) + ctx, err := egl.NewContext(disp) + if err != nil { + return nil, err + } + return &x11Context{win: w, Context: ctx}, nil + } +} + +func (c *x11Context) Release() { + if c.Context != nil { + c.Context.Release() + c.Context = nil + } +} + +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 +} + +func (c *x11Context) Lock() error { + return c.Context.MakeCurrent() +} + +func (c *x11Context) Unlock() { + c.Context.ReleaseCurrent() +} diff --git a/vendor/gioui.org/app/internal/window/framework_ios.h b/vendor/gioui.org/app/framework_ios.h index 18e5a02..18e5a02 100644 --- a/vendor/gioui.org/app/internal/window/framework_ios.h +++ b/vendor/gioui.org/app/framework_ios.h diff --git a/vendor/gioui.org/app/internal/window/gl_ios.go b/vendor/gioui.org/app/gl_ios.go index 0099189..2dc2dbf 100644 --- a/vendor/gioui.org/app/internal/window/gl_ios.go +++ b/vendor/gioui.org/app/gl_ios.go @@ -1,10 +1,13 @@ // SPDX-License-Identifier: Unlicense OR MIT -// +build darwin,ios +//go:build darwin && ios && nometal +// +build darwin,ios,nometal -package window +package app /* +@import UIKit; + #include <CoreFoundation/CoreFoundation.h> #include <OpenGLES/ES2/gl.h> #include <OpenGLES/ES2/glext.h> @@ -14,32 +17,34 @@ __attribute__ ((visibility ("hidden"))) int gio_presentRenderbuffer(CFTypeRef ct __attribute__ ((visibility ("hidden"))) int gio_makeCurrent(CFTypeRef ctx); __attribute__ ((visibility ("hidden"))) CFTypeRef gio_createContext(void); __attribute__ ((visibility ("hidden"))) CFTypeRef gio_createGLLayer(void); + +static CFTypeRef getViewLayer(CFTypeRef viewRef) { + @autoreleasepool { + UIView *view = (__bridge UIView *)viewRef; + return CFBridgingRetain(view.layer); + } +} + */ import "C" import ( "errors" "fmt" + "runtime" - "gioui.org/app/internal/glimpl" - "gioui.org/gpu/backend" - "gioui.org/gpu/gl" + "gioui.org/gpu" + "gioui.org/internal/gl" ) type context struct { - owner *window - c *glimpl.Functions - ctx C.CFTypeRef - layer C.CFTypeRef - init bool - frameBuffer gl.Framebuffer - colorBuffer, depthBuffer gl.Renderbuffer -} - -func init() { - layerFactory = func() uintptr { - return uintptr(C.gio_createGLLayer()) - } + owner *window + c *gl.Functions + ctx C.CFTypeRef + layer C.CFTypeRef + init bool + frameBuffer gl.Framebuffer + colorBuffer gl.Renderbuffer } func newContext(w *window) (*context, error) { @@ -47,17 +52,30 @@ func newContext(w *window) (*context, error) { if ctx == 0 { return nil, fmt.Errorf("failed to create EAGLContext") } + api := contextAPI() + f, err := gl.NewFunctions(api.Context, api.ES) + if err != nil { + return nil, err + } c := &context{ ctx: ctx, owner: w, - layer: C.CFTypeRef(w.contextLayer()), - c: new(glimpl.Functions), + layer: C.getViewLayer(w.contextView()), + c: f, } return c, nil } -func (c *context) Backend() (backend.Device, error) { - return gl.NewBackend(c.c) +func contextAPI() gpu.OpenGL { + return gpu.OpenGL{} +} + +func (c *context) RenderTarget() gpu.RenderTarget { + return gpu.OpenGLRenderTarget(c.frameBuffer) +} + +func (c *context) API() gpu.API { + return contextAPI() } func (c *context) Release() { @@ -67,7 +85,6 @@ func (c *context) Release() { C.gio_renderbufferStorage(c.ctx, 0, C.GLenum(gl.RENDERBUFFER)) c.c.DeleteFramebuffer(c.frameBuffer) c.c.DeleteRenderbuffer(c.colorBuffer) - c.c.DeleteRenderbuffer(c.depthBuffer) C.gio_makeCurrent(0) C.CFRelease(c.ctx) c.ctx = 0 @@ -77,10 +94,6 @@ func (c *context) Present() error { if c.layer == 0 { panic("context is not active") } - // Discard depth buffer as recommended in - // https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/OpenGLES_ProgrammingGuide/WorkingwithEAGLContexts/WorkingwithEAGLContexts.html - c.c.BindFramebuffer(gl.FRAMEBUFFER, c.frameBuffer) - c.c.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT) c.c.BindRenderbuffer(gl.RENDERBUFFER, c.colorBuffer) if C.gio_presentRenderbuffer(c.ctx, C.GLenum(gl.RENDERBUFFER)) == 0 { return errors.New("presentRenderBuffer failed") @@ -88,23 +101,30 @@ func (c *context) Present() error { return nil } -func (c *context) Lock() {} +func (c *context) Lock() error { + // OpenGL contexts are implicit and thread-local. Lock the OS thread. + runtime.LockOSThread() -func (c *context) Unlock() {} + if C.gio_makeCurrent(c.ctx) == 0 { + return errors.New("[EAGLContext setCurrentContext] failed") + } + return nil +} + +func (c *context) Unlock() { + C.gio_makeCurrent(0) +} -func (c *context) MakeCurrent() error { +func (c *context) Refresh() error { if C.gio_makeCurrent(c.ctx) == 0 { - C.CFRelease(c.ctx) - c.ctx = 0 return errors.New("[EAGLContext setCurrentContext] failed") } if !c.init { c.init = true c.frameBuffer = c.c.CreateFramebuffer() c.colorBuffer = c.c.CreateRenderbuffer() - c.depthBuffer = c.c.CreateRenderbuffer() } - if !c.owner.isVisible() { + if !c.owner.visible { // Make sure any in-flight GL commands are complete. c.c.Finish() return nil @@ -114,14 +134,9 @@ func (c *context) MakeCurrent() error { if C.gio_renderbufferStorage(c.ctx, c.layer, C.GLenum(gl.RENDERBUFFER)) == 0 { return errors.New("renderbufferStorage failed") } - w := c.c.GetRenderbufferParameteri(gl.RENDERBUFFER, gl.RENDERBUFFER_WIDTH) - h := c.c.GetRenderbufferParameteri(gl.RENDERBUFFER, gl.RENDERBUFFER_HEIGHT) - c.c.BindRenderbuffer(gl.RENDERBUFFER, c.depthBuffer) - c.c.RenderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, w, h) c.c.BindRenderbuffer(gl.RENDERBUFFER, currentRB) c.c.BindFramebuffer(gl.FRAMEBUFFER, c.frameBuffer) c.c.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, c.colorBuffer) - c.c.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, c.depthBuffer) if st := c.c.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE { return fmt.Errorf("framebuffer incomplete, status: %#x\n", st) } diff --git a/vendor/gioui.org/app/internal/window/gl_ios.m b/vendor/gioui.org/app/gl_ios.m index 065ea97..8e50753 100644 --- a/vendor/gioui.org/app/internal/window/gl_ios.m +++ b/vendor/gioui.org/app/gl_ios.m @@ -1,12 +1,16 @@ // SPDX-License-Identifier: Unlicense OR MIT -// +build darwin,ios +// +build darwin,ios,nometal @import UIKit; @import OpenGLES; #include "_cgo_export.h" +Class gio_layerClass(void) { + return [CAEAGLLayer class]; +} + int gio_renderbufferStorage(CFTypeRef ctxRef, CFTypeRef layerRef, GLenum buffer) { EAGLContext *ctx = (__bridge EAGLContext *)ctxRef; CAEAGLLayer *layer = (__bridge CAEAGLLayer *)layerRef; diff --git a/vendor/gioui.org/app/gl_js.go b/vendor/gioui.org/app/gl_js.go new file mode 100644 index 0000000..e3aee8d --- /dev/null +++ b/vendor/gioui.org/app/gl_js.go @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package app + +import ( + "errors" + "syscall/js" + + "gioui.org/gpu" + "gioui.org/internal/gl" +) + +type glContext struct { + ctx js.Value + cnv js.Value +} + +func newContext(w *window) (*glContext, error) { + args := map[string]interface{}{ + // Enable low latency rendering. + // See https://developers.google.com/web/updates/2019/05/desynchronized. + "desynchronized": true, + "preserveDrawingBuffer": true, + } + ctx := w.cnv.Call("getContext", "webgl2", args) + if ctx.IsNull() { + ctx = w.cnv.Call("getContext", "webgl", args) + } + if ctx.IsNull() { + return nil, errors.New("app: webgl is not supported") + } + c := &glContext{ + ctx: ctx, + cnv: w.cnv, + } + return c, nil +} + +func (c *glContext) RenderTarget() (gpu.RenderTarget, error) { + return gpu.OpenGLRenderTarget{}, nil +} + +func (c *glContext) API() gpu.API { + return gpu.OpenGL{Context: gl.Context(c.ctx)} +} + +func (c *glContext) Release() { +} + +func (c *glContext) Present() error { + if c.ctx.Call("isContextLost").Bool() { + return errors.New("context lost") + } + return nil +} + +func (c *glContext) Lock() error { + return nil +} + +func (c *glContext) Unlock() {} + +func (c *glContext) Refresh() error { + return nil +} + +func (w *window) NewContext() (context, error) { + return newContext(w) +} diff --git a/vendor/gioui.org/app/gl_macos.go b/vendor/gioui.org/app/gl_macos.go new file mode 100644 index 0000000..7511bbd --- /dev/null +++ b/vendor/gioui.org/app/gl_macos.go @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build darwin && !ios && nometal +// +build darwin,!ios,nometal + +package app + +import ( + "errors" + "runtime" + + "unsafe" + + "gioui.org/gpu" + "gioui.org/internal/gl" +) + +/* +#include <CoreFoundation/CoreFoundation.h> +#include <CoreGraphics/CoreGraphics.h> +#include <AppKit/AppKit.h> +#include <dlfcn.h> + +__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createGLContext(void); +__attribute__ ((visibility ("hidden"))) void gio_setContextView(CFTypeRef ctx, CFTypeRef view); +__attribute__ ((visibility ("hidden"))) void gio_makeCurrentContext(CFTypeRef ctx); +__attribute__ ((visibility ("hidden"))) void gio_updateContext(CFTypeRef ctx); +__attribute__ ((visibility ("hidden"))) void gio_flushContextBuffer(CFTypeRef ctx); +__attribute__ ((visibility ("hidden"))) void gio_clearCurrentContext(void); +__attribute__ ((visibility ("hidden"))) void gio_lockContext(CFTypeRef ctxRef); +__attribute__ ((visibility ("hidden"))) void gio_unlockContext(CFTypeRef ctxRef); + +typedef void (*PFN_glFlush)(void); + +static void glFlush(PFN_glFlush f) { + f(); +} +*/ +import "C" + +type glContext struct { + c *gl.Functions + ctx C.CFTypeRef + view C.CFTypeRef + + glFlush C.PFN_glFlush +} + +func newContext(w *window) (*glContext, error) { + clib := C.CString("/System/Library/Frameworks/OpenGL.framework/OpenGL") + defer C.free(unsafe.Pointer(clib)) + lib, err := C.dlopen(clib, C.RTLD_NOW|C.RTLD_LOCAL) + if err != nil { + return nil, err + } + csym := C.CString("glFlush") + defer C.free(unsafe.Pointer(csym)) + glFlush := C.PFN_glFlush(C.dlsym(lib, csym)) + if glFlush == nil { + return nil, errors.New("gl: missing symbol glFlush in the OpenGL framework") + } + view := w.contextView() + ctx := C.gio_createGLContext() + if ctx == 0 { + return nil, errors.New("gl: failed to create NSOpenGLContext") + } + C.gio_setContextView(ctx, view) + c := &glContext{ + ctx: ctx, + view: view, + glFlush: glFlush, + } + return c, nil +} + +func (c *glContext) RenderTarget() (gpu.RenderTarget, error) { + return gpu.OpenGLRenderTarget{}, nil +} + +func (c *glContext) API() gpu.API { + return gpu.OpenGL{} +} + +func (c *glContext) Release() { + if c.ctx != 0 { + C.gio_clearCurrentContext() + C.CFRelease(c.ctx) + c.ctx = 0 + } +} + +func (c *glContext) Present() error { + // Assume the caller already locked the context. + C.glFlush(c.glFlush) + return nil +} + +func (c *glContext) Lock() error { + // OpenGL contexts are implicit and thread-local. Lock the OS thread. + runtime.LockOSThread() + + C.gio_lockContext(c.ctx) + C.gio_makeCurrentContext(c.ctx) + return nil +} + +func (c *glContext) Unlock() { + C.gio_clearCurrentContext() + C.gio_unlockContext(c.ctx) +} + +func (c *glContext) Refresh() error { + c.Lock() + defer c.Unlock() + C.gio_updateContext(c.ctx) + return nil +} + +func (w *window) NewContext() (context, error) { + return newContext(w) +} diff --git a/vendor/gioui.org/app/gl_macos.m b/vendor/gioui.org/app/gl_macos.m new file mode 100644 index 0000000..fe83743 --- /dev/null +++ b/vendor/gioui.org/app/gl_macos.m @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +// +build darwin,!ios,nometal + +@import AppKit; + +#include <CoreFoundation/CoreFoundation.h> +#include <OpenGL/OpenGL.h> +#include "_cgo_export.h" + +CALayer *gio_layerFactory(void) { + @autoreleasepool { + return [CALayer layer]; + } +} + +CFTypeRef gio_createGLContext(void) { + @autoreleasepool { + NSOpenGLPixelFormatAttribute attr[] = { + NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, + NSOpenGLPFAColorSize, 24, + NSOpenGLPFAAccelerated, + // Opt-in to automatic GPU switching. CGL-only property. + kCGLPFASupportsAutomaticGraphicsSwitching, + NSOpenGLPFAAllowOfflineRenderers, + 0 + }; + NSOpenGLPixelFormat *pixFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr]; + + NSOpenGLContext *ctx = [[NSOpenGLContext alloc] initWithFormat:pixFormat shareContext: nil]; + return CFBridgingRetain(ctx); + } +} + +void gio_setContextView(CFTypeRef ctxRef, CFTypeRef viewRef) { + NSOpenGLContext *ctx = (__bridge NSOpenGLContext *)ctxRef; + NSView *view = (__bridge NSView *)viewRef; + [view setWantsBestResolutionOpenGLSurface:YES]; + [ctx setView:view]; +} + +void gio_clearCurrentContext(void) { + @autoreleasepool { + [NSOpenGLContext clearCurrentContext]; + } +} + +void gio_updateContext(CFTypeRef ctxRef) { + @autoreleasepool { + NSOpenGLContext *ctx = (__bridge NSOpenGLContext *)ctxRef; + [ctx update]; + } +} + +void gio_makeCurrentContext(CFTypeRef ctxRef) { + @autoreleasepool { + NSOpenGLContext *ctx = (__bridge NSOpenGLContext *)ctxRef; + [ctx makeCurrentContext]; + } +} + +void gio_lockContext(CFTypeRef ctxRef) { + @autoreleasepool { + NSOpenGLContext *ctx = (__bridge NSOpenGLContext *)ctxRef; + CGLLockContext([ctx CGLContextObj]); + } +} + +void gio_unlockContext(CFTypeRef ctxRef) { + @autoreleasepool { + NSOpenGLContext *ctx = (__bridge NSOpenGLContext *)ctxRef; + CGLUnlockContext([ctx CGLContextObj]); + } +} diff --git a/vendor/gioui.org/app/internal/cocoainit/cocoa_darwin.go b/vendor/gioui.org/app/internal/cocoainit/cocoa_darwin.go deleted file mode 100644 index 2a34e57..0000000 --- a/vendor/gioui.org/app/internal/cocoainit/cocoa_darwin.go +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -// Package cocoainit initializes support for multithreaded -// programs in Cocoa. -package cocoainit - -/* -#cgo CFLAGS: -xobjective-c -fmodules -fobjc-arc -#import <Foundation/Foundation.h> - -static inline void activate_cocoa_multithreading() { - [[NSThread new] start]; -} -#pragma GCC visibility push(hidden) -*/ -import "C" - -func init() { - C.activate_cocoa_multithreading() -} diff --git a/vendor/gioui.org/app/internal/d3d11/backend_windows.go b/vendor/gioui.org/app/internal/d3d11/backend_windows.go deleted file mode 100644 index 48ff175..0000000 --- a/vendor/gioui.org/app/internal/d3d11/backend_windows.go +++ /dev/null @@ -1,856 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package d3d11 - -import ( - "errors" - "fmt" - "image" - "math" - "unsafe" - - "gioui.org/gpu/backend" - gunsafe "gioui.org/internal/unsafe" - "golang.org/x/sys/windows" -) - -const debug = false - -type Device struct { - dev *_ID3D11Device - ctx *_ID3D11DeviceContext - featLvl uint32 - floatFormat uint32 - depthStates map[depthState]*_ID3D11DepthStencilState - blendStates map[blendState]*_ID3D11BlendState -} - -type Backend struct { - // Temporary storage to avoid garbage. - clearColor [4]float32 - viewport _D3D11_VIEWPORT - depthState depthState - blendState blendState - prog *Program - - dev *Device - caps backend.Caps - - // fbo is the currently bound fbo. - fbo *Framebuffer -} - -type blendState struct { - enable bool - sfactor backend.BlendFactor - dfactor backend.BlendFactor -} - -type depthState struct { - enable bool - mask bool - fn backend.DepthFunc -} - -type Texture struct { - backend *Backend - format uint32 - bindings backend.BufferBinding - tex *_ID3D11Texture2D - sampler *_ID3D11SamplerState - resView *_ID3D11ShaderResourceView - width int - height int -} - -type Program struct { - backend *Backend - - vert struct { - shader *_ID3D11VertexShader - uniforms *Buffer - } - frag struct { - shader *_ID3D11PixelShader - uniforms *Buffer - } -} - -type Framebuffer struct { - dev *Device - format uint32 - resource *_ID3D11Resource - renderTarget *_ID3D11RenderTargetView - depthView *_ID3D11DepthStencilView - foreign bool -} - -type Buffer struct { - backend *Backend - bind uint32 - buf *_ID3D11Buffer - immutable bool -} - -type InputLayout struct { - dev *Device - layout *_ID3D11InputLayout -} - -type SwapChain struct { - swchain *_IDXGISwapChain - fbo *Framebuffer -} - -func NewDevice() (*Device, error) { - var flags uint32 - if debug { - flags |= _D3D11_CREATE_DEVICE_DEBUG - } - d3ddev, d3dctx, featLvl, err := _D3D11CreateDevice( - _D3D_DRIVER_TYPE_HARDWARE, - flags, - ) - if err != nil { - return nil, fmt.Errorf("NewContext: %v", err) - } - dev := &Device{dev: d3ddev, ctx: d3dctx, featLvl: featLvl} - if featLvl < _D3D_FEATURE_LEVEL_9_1 { - _IUnknownRelease(unsafe.Pointer(d3ddev), d3ddev.vtbl.Release) - _IUnknownRelease(unsafe.Pointer(d3dctx), d3dctx.vtbl.Release) - return nil, fmt.Errorf("d3d11: feature level too low: %d", featLvl) - } - floatFormat, ok := detectFloatFormat(d3ddev) - if !ok { - _IUnknownRelease(unsafe.Pointer(d3ddev), d3ddev.vtbl.Release) - _IUnknownRelease(unsafe.Pointer(d3dctx), d3dctx.vtbl.Release) - return nil, fmt.Errorf("d3d11: no available floating point formats") - } - dev.floatFormat = floatFormat - dev.depthStates = make(map[depthState]*_ID3D11DepthStencilState) - dev.blendStates = make(map[blendState]*_ID3D11BlendState) - return dev, nil -} - -func detectFloatFormat(dev *_ID3D11Device) (uint32, bool) { - formats := []uint32{ - _DXGI_FORMAT_R16_FLOAT, - _DXGI_FORMAT_R32_FLOAT, - _DXGI_FORMAT_R16G16_FLOAT, - _DXGI_FORMAT_R32G32_FLOAT, - // These last two are really wasteful, but c'est la vie. - _DXGI_FORMAT_R16G16B16A16_FLOAT, - _DXGI_FORMAT_R32G32B32A32_FLOAT, - } - for _, format := range formats { - need := uint32(_D3D11_FORMAT_SUPPORT_TEXTURE2D | _D3D11_FORMAT_SUPPORT_RENDER_TARGET) - if support, _ := dev.CheckFormatSupport(format); support&need == need { - return format, true - } - } - return 0, false -} - -func (d *Device) CreateSwapChain(hwnd windows.Handle) (*SwapChain, error) { - dxgiDev, err := _IUnknownQueryInterface(unsafe.Pointer(d.dev), d.dev.vtbl.QueryInterface, &_IID_IDXGIDevice) - if err != nil { - return nil, fmt.Errorf("NewContext: %v", err) - } - adapter, err := (*_IDXGIDevice)(unsafe.Pointer(dxgiDev)).GetAdapter() - _IUnknownRelease(unsafe.Pointer(dxgiDev), dxgiDev.vtbl.Release) - if err != nil { - return nil, fmt.Errorf("NewContext: %v", err) - } - dxgiFactory, err := (*_IDXGIObject)(unsafe.Pointer(adapter)).GetParent(&_IID_IDXGIFactory) - _IUnknownRelease(unsafe.Pointer(adapter), adapter.vtbl.Release) - if err != nil { - return nil, fmt.Errorf("NewContext: %v", err) - } - d3dswchain, err := (*_IDXGIFactory)(unsafe.Pointer(dxgiFactory)).CreateSwapChain( - (*_IUnknown)(unsafe.Pointer(d.dev)), - &_DXGI_SWAP_CHAIN_DESC{ - BufferDesc: _DXGI_MODE_DESC{ - Format: _DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, - }, - SampleDesc: _DXGI_SAMPLE_DESC{ - Count: 1, - }, - BufferUsage: _DXGI_USAGE_RENDER_TARGET_OUTPUT, - BufferCount: 1, - OutputWindow: hwnd, - Windowed: 1, - SwapEffect: _DXGI_SWAP_EFFECT_DISCARD, - }, - ) - _IUnknownRelease(unsafe.Pointer(dxgiFactory), dxgiFactory.vtbl.Release) - if err != nil { - return nil, fmt.Errorf("NewContext: %v", err) - } - return &SwapChain{swchain: d3dswchain, fbo: &Framebuffer{}}, nil -} - -func (s *SwapChain) Framebuffer(d *Device) (*Framebuffer, error) { - if s.fbo.renderTarget != nil { - return s.fbo, nil - } - desc, err := s.swchain.GetDesc() - if err != nil { - return nil, err - } - backBuffer, err := s.swchain.GetBuffer(0, &_IID_ID3D11Texture2D) - if err != nil { - return nil, err - } - texture := (*_ID3D11Resource)(unsafe.Pointer(backBuffer)) - renderTarget, err := d.dev.CreateRenderTargetView(texture) - _IUnknownRelease(unsafe.Pointer(backBuffer), backBuffer.vtbl.Release) - if err != nil { - return nil, err - } - depthView, err := createDepthView(d.dev, int(desc.BufferDesc.Width), int(desc.BufferDesc.Height), 24) - if err != nil { - _IUnknownRelease(unsafe.Pointer(renderTarget), renderTarget.vtbl.Release) - return nil, err - } - s.fbo.renderTarget = renderTarget - s.fbo.depthView = depthView - s.fbo.dev = d - return s.fbo, nil -} - -func (d *Device) Release() { - _IUnknownRelease(unsafe.Pointer(d.ctx), d.ctx.vtbl.Release) - _IUnknownRelease(unsafe.Pointer(d.dev), d.dev.vtbl.Release) - d.ctx = nil - d.dev = nil - for _, state := range d.depthStates { - _IUnknownRelease(unsafe.Pointer(state), state.vtbl.Release) - } - d.depthStates = nil - for _, state := range d.blendStates { - _IUnknownRelease(unsafe.Pointer(state), state.vtbl.Release) - } - d.blendStates = nil -} - -func (s *SwapChain) Resize() error { - if s.fbo.renderTarget != nil { - s.fbo.Release() - } - return s.swchain.ResizeBuffers(0, 0, 0, _DXGI_FORMAT_UNKNOWN, 0) -} - -func (s *SwapChain) Release() { - _IUnknownRelease(unsafe.Pointer(s.swchain), s.swchain.vtbl.Release) -} - -func (s *SwapChain) Present() error { - return s.swchain.Present(1, 0) -} - -func NewBackend(d *Device) (*Backend, error) { - caps := backend.Caps{ - MaxTextureSize: 2048, // 9.1 maximum - } - switch { - case d.featLvl >= _D3D_FEATURE_LEVEL_11_0: - caps.MaxTextureSize = 16384 - case d.featLvl >= _D3D_FEATURE_LEVEL_9_3: - caps.MaxTextureSize = 4096 - } - b := &Backend{dev: d, caps: caps} - // Disable backface culling to match OpenGL. - state, err := b.dev.dev.CreateRasterizerState(&_D3D11_RASTERIZER_DESC{ - CullMode: _D3D11_CULL_NONE, - FillMode: _D3D11_FILL_SOLID, - DepthClipEnable: 1, - }) - // Enable depth mask to match OpenGL. - b.depthState.mask = true - if err != nil { - return nil, err - } - b.dev.ctx.RSSetState(state) - _IUnknownRelease(unsafe.Pointer(state), state.vtbl.Release) - return b, nil -} - -func (b *Backend) BeginFrame() { -} - -func (b *Backend) EndFrame() { -} - -func (b *Backend) Caps() backend.Caps { - return b.caps -} - -func (b *Backend) NewTimer() backend.Timer { - panic("timers not supported") -} - -func (b *Backend) IsTimeContinuous() bool { - panic("timers not supported") -} - -func (b *Backend) NewTexture(format backend.TextureFormat, width, height int, minFilter, magFilter backend.TextureFilter, bindings backend.BufferBinding) (backend.Texture, error) { - var d3dfmt uint32 - switch format { - case backend.TextureFormatFloat: - d3dfmt = b.dev.floatFormat - case backend.TextureFormatSRGB: - d3dfmt = _DXGI_FORMAT_R8G8B8A8_UNORM_SRGB - default: - return nil, fmt.Errorf("unsupported texture format %d", format) - } - tex, err := b.dev.dev.CreateTexture2D(&_D3D11_TEXTURE2D_DESC{ - Width: uint32(width), - Height: uint32(height), - MipLevels: 1, - ArraySize: 1, - Format: d3dfmt, - SampleDesc: _DXGI_SAMPLE_DESC{ - Count: 1, - Quality: 0, - }, - BindFlags: convBufferBinding(bindings), - }) - if err != nil { - return nil, err - } - var ( - sampler *_ID3D11SamplerState - resView *_ID3D11ShaderResourceView - ) - if bindings&backend.BufferBindingTexture != 0 { - var filter uint32 - switch { - case minFilter == backend.FilterNearest && magFilter == backend.FilterNearest: - filter = _D3D11_FILTER_MIN_MAG_MIP_POINT - case minFilter == backend.FilterLinear && magFilter == backend.FilterLinear: - filter = _D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT - default: - _IUnknownRelease(unsafe.Pointer(tex), tex.vtbl.Release) - return nil, fmt.Errorf("unsupported texture filter combination %d, %d", minFilter, magFilter) - } - var err error - sampler, err = b.dev.dev.CreateSamplerState(&_D3D11_SAMPLER_DESC{ - Filter: filter, - AddressU: _D3D11_TEXTURE_ADDRESS_CLAMP, - AddressV: _D3D11_TEXTURE_ADDRESS_CLAMP, - AddressW: _D3D11_TEXTURE_ADDRESS_CLAMP, - MaxAnisotropy: 1, - MinLOD: -math.MaxFloat32, - MaxLOD: math.MaxFloat32, - }) - if err != nil { - _IUnknownRelease(unsafe.Pointer(tex), tex.vtbl.Release) - return nil, err - } - resView, err = b.dev.dev.CreateShaderResourceViewTEX2D( - (*_ID3D11Resource)(unsafe.Pointer(tex)), - &_D3D11_SHADER_RESOURCE_VIEW_DESC_TEX2D{ - _D3D11_SHADER_RESOURCE_VIEW_DESC: _D3D11_SHADER_RESOURCE_VIEW_DESC{ - Format: d3dfmt, - ViewDimension: _D3D11_SRV_DIMENSION_TEXTURE2D, - }, - Texture2D: _D3D11_TEX2D_SRV{ - MostDetailedMip: 0, - MipLevels: ^uint32(0), - }, - }, - ) - if err != nil { - _IUnknownRelease(unsafe.Pointer(tex), tex.vtbl.Release) - _IUnknownRelease(unsafe.Pointer(sampler), sampler.vtbl.Release) - return nil, err - } - } - return &Texture{backend: b, format: d3dfmt, tex: tex, sampler: sampler, resView: resView, bindings: bindings, width: width, height: height}, nil -} - -func (b *Backend) CurrentFramebuffer() backend.Framebuffer { - renderTarget := b.dev.ctx.OMGetRenderTargets() - if renderTarget != nil { - // Assume someone else is holding on to it. - _IUnknownRelease(unsafe.Pointer(renderTarget), renderTarget.vtbl.Release) - } - if renderTarget == b.fbo.renderTarget { - return b.fbo - } - return &Framebuffer{dev: b.dev, renderTarget: renderTarget, foreign: true} -} - -func (b *Backend) NewFramebuffer(tex backend.Texture, depthBits int) (backend.Framebuffer, error) { - d3dtex := tex.(*Texture) - if d3dtex.bindings&backend.BufferBindingFramebuffer == 0 { - return nil, errors.New("the texture was created without BufferBindingFramebuffer binding") - } - resource := (*_ID3D11Resource)(unsafe.Pointer(d3dtex.tex)) - renderTarget, err := b.dev.dev.CreateRenderTargetView(resource) - if err != nil { - return nil, err - } - fbo := &Framebuffer{dev: b.dev, format: d3dtex.format, resource: resource, renderTarget: renderTarget} - if depthBits > 0 { - depthView, err := createDepthView(b.dev.dev, d3dtex.width, d3dtex.height, depthBits) - if err != nil { - _IUnknownRelease(unsafe.Pointer(renderTarget), renderTarget.vtbl.Release) - return nil, err - } - fbo.depthView = depthView - } - return fbo, nil -} - -func createDepthView(d *_ID3D11Device, width, height, depthBits int) (*_ID3D11DepthStencilView, error) { - depthTex, err := d.CreateTexture2D(&_D3D11_TEXTURE2D_DESC{ - Width: uint32(width), - Height: uint32(height), - MipLevels: 1, - ArraySize: 1, - Format: _DXGI_FORMAT_D24_UNORM_S8_UINT, - SampleDesc: _DXGI_SAMPLE_DESC{ - Count: 1, - Quality: 0, - }, - BindFlags: _D3D11_BIND_DEPTH_STENCIL, - }) - if err != nil { - return nil, err - } - depthView, err := d.CreateDepthStencilViewTEX2D( - (*_ID3D11Resource)(unsafe.Pointer(depthTex)), - &_D3D11_DEPTH_STENCIL_VIEW_DESC_TEX2D{ - Format: _DXGI_FORMAT_D24_UNORM_S8_UINT, - ViewDimension: _D3D11_DSV_DIMENSION_TEXTURE2D, - }, - ) - _IUnknownRelease(unsafe.Pointer(depthTex), depthTex.vtbl.Release) - return depthView, err -} - -func (b *Backend) NewInputLayout(vertexShader backend.ShaderSources, layout []backend.InputDesc) (backend.InputLayout, error) { - if len(vertexShader.Inputs) != len(layout) { - return nil, fmt.Errorf("NewInputLayout: got %d inputs, expected %d", len(layout), len(vertexShader.Inputs)) - } - descs := make([]_D3D11_INPUT_ELEMENT_DESC, len(layout)) - for i, l := range layout { - inp := vertexShader.Inputs[i] - cname, err := windows.BytePtrFromString(inp.Semantic) - if err != nil { - return nil, err - } - var format uint32 - switch l.Type { - case backend.DataTypeFloat: - switch l.Size { - case 1: - format = _DXGI_FORMAT_R32_FLOAT - case 2: - format = _DXGI_FORMAT_R32G32_FLOAT - case 3: - format = _DXGI_FORMAT_R32G32B32_FLOAT - case 4: - format = _DXGI_FORMAT_R32G32B32A32_FLOAT - default: - panic("unsupported float data size") - } - case backend.DataTypeShort: - switch l.Size { - case 1: - format = _DXGI_FORMAT_R16_SINT - case 2: - format = _DXGI_FORMAT_R16G16_SINT - default: - panic("unsupported float data size") - } - default: - panic("unsupported data type") - } - descs[i] = _D3D11_INPUT_ELEMENT_DESC{ - SemanticName: cname, - SemanticIndex: uint32(inp.SemanticIndex), - Format: format, - AlignedByteOffset: uint32(l.Offset), - } - } - l, err := b.dev.dev.CreateInputLayout(descs, vertexShader.HLSL) - if err != nil { - return nil, err - } - return &InputLayout{dev: b.dev, layout: l}, nil -} - -func (b *Backend) NewBuffer(typ backend.BufferBinding, size int) (backend.Buffer, error) { - if typ&backend.BufferBindingUniforms != 0 { - if typ != backend.BufferBindingUniforms { - return nil, errors.New("uniform buffers cannot have other bindings") - } - if size%16 != 0 { - return nil, fmt.Errorf("constant buffer size is %d, expected a multiple of 16", size) - } - } - bind := convBufferBinding(typ) - buf, err := b.dev.dev.CreateBuffer(&_D3D11_BUFFER_DESC{ - ByteWidth: uint32(size), - BindFlags: bind, - }, nil) - if err != nil { - return nil, err - } - return &Buffer{backend: b, buf: buf, bind: bind}, nil -} - -func (b *Backend) NewImmutableBuffer(typ backend.BufferBinding, data []byte) (backend.Buffer, error) { - if typ&backend.BufferBindingUniforms != 0 { - if typ != backend.BufferBindingUniforms { - return nil, errors.New("uniform buffers cannot have other bindings") - } - if len(data)%16 != 0 { - return nil, fmt.Errorf("constant buffer size is %d, expected a multiple of 16", len(data)) - } - } - bind := convBufferBinding(typ) - buf, err := b.dev.dev.CreateBuffer(&_D3D11_BUFFER_DESC{ - ByteWidth: uint32(len(data)), - Usage: _D3D11_USAGE_IMMUTABLE, - BindFlags: bind, - }, data) - if err != nil { - return nil, err - } - return &Buffer{backend: b, buf: buf, bind: bind, immutable: true}, nil -} - -func (b *Backend) NewProgram(vertexShader, fragmentShader backend.ShaderSources) (backend.Program, error) { - vs, err := b.dev.dev.CreateVertexShader(vertexShader.HLSL) - if err != nil { - return nil, err - } - ps, err := b.dev.dev.CreatePixelShader(fragmentShader.HLSL) - if err != nil { - return nil, err - } - p := &Program{backend: b} - p.vert.shader = vs - p.frag.shader = ps - return p, nil -} - -func (b *Backend) Clear(colr, colg, colb, cola float32) { - b.clearColor = [4]float32{colr, colg, colb, cola} - b.dev.ctx.ClearRenderTargetView(b.fbo.renderTarget, &b.clearColor) -} - -func (b *Backend) ClearDepth(depth float32) { - if b.fbo.depthView != nil { - b.dev.ctx.ClearDepthStencilView(b.fbo.depthView, _D3D11_CLEAR_DEPTH|_D3D11_CLEAR_STENCIL, depth, 0) - } -} - -func (b *Backend) Viewport(x, y, width, height int) { - b.viewport = _D3D11_VIEWPORT{ - TopLeftX: float32(x), - TopLeftY: float32(y), - Width: float32(width), - Height: float32(height), - MinDepth: 0.0, - MaxDepth: 1.0, - } - b.dev.ctx.RSSetViewports(&b.viewport) -} - -func (b *Backend) DrawArrays(mode backend.DrawMode, off, count int) { - b.prepareDraw(mode) - b.dev.ctx.Draw(uint32(count), uint32(off)) -} - -func (b *Backend) DrawElements(mode backend.DrawMode, off, count int) { - b.prepareDraw(mode) - b.dev.ctx.DrawIndexed(uint32(count), uint32(off), 0) -} - -func (b *Backend) prepareDraw(mode backend.DrawMode) { - if p := b.prog; p != nil { - b.dev.ctx.VSSetShader(p.vert.shader) - b.dev.ctx.PSSetShader(p.frag.shader) - if buf := p.vert.uniforms; buf != nil { - b.dev.ctx.VSSetConstantBuffers(buf.buf) - } - if buf := p.frag.uniforms; buf != nil { - b.dev.ctx.PSSetConstantBuffers(buf.buf) - } - } - var topology uint32 - switch mode { - case backend.DrawModeTriangles: - topology = _D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST - case backend.DrawModeTriangleStrip: - topology = _D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP - default: - panic("unsupported draw mode") - } - b.dev.ctx.IASetPrimitiveTopology(topology) - - depthState, ok := b.dev.depthStates[b.depthState] - if !ok { - var desc _D3D11_DEPTH_STENCIL_DESC - if b.depthState.enable { - desc.DepthEnable = 1 - } - if b.depthState.mask { - desc.DepthWriteMask = _D3D11_DEPTH_WRITE_MASK_ALL - } - switch b.depthState.fn { - case backend.DepthFuncGreater: - desc.DepthFunc = _D3D11_COMPARISON_GREATER - case backend.DepthFuncGreaterEqual: - desc.DepthFunc = _D3D11_COMPARISON_GREATER_EQUAL - default: - panic("unsupported depth func") - } - var err error - depthState, err = b.dev.dev.CreateDepthStencilState(&desc) - if err != nil { - panic(err) - } - b.dev.depthStates[b.depthState] = depthState - } - b.dev.ctx.OMSetDepthStencilState(depthState, 0) - - blendState, ok := b.dev.blendStates[b.blendState] - if !ok { - var desc _D3D11_BLEND_DESC - t0 := &desc.RenderTarget[0] - t0.RenderTargetWriteMask = _D3D11_COLOR_WRITE_ENABLE_ALL - t0.BlendOp = _D3D11_BLEND_OP_ADD - t0.BlendOpAlpha = _D3D11_BLEND_OP_ADD - if b.blendState.enable { - t0.BlendEnable = 1 - } - scol, salpha := toBlendFactor(b.blendState.sfactor) - dcol, dalpha := toBlendFactor(b.blendState.dfactor) - t0.SrcBlend = scol - t0.SrcBlendAlpha = salpha - t0.DestBlend = dcol - t0.DestBlendAlpha = dalpha - var err error - blendState, err = b.dev.dev.CreateBlendState(&desc) - if err != nil { - panic(err) - } - b.dev.blendStates[b.blendState] = blendState - } - b.dev.ctx.OMSetBlendState(blendState, nil, 0xffffffff) -} - -func (b *Backend) DepthFunc(f backend.DepthFunc) { - b.depthState.fn = f -} - -func (b *Backend) SetBlend(enable bool) { - b.blendState.enable = enable -} - -func (b *Backend) SetDepthTest(enable bool) { - b.depthState.enable = enable -} - -func (b *Backend) DepthMask(mask bool) { - b.depthState.mask = mask -} - -func (b *Backend) BlendFunc(sfactor, dfactor backend.BlendFactor) { - b.blendState.sfactor = sfactor - b.blendState.dfactor = dfactor -} - -func (t *Texture) Upload(img *image.RGBA) { - b := img.Bounds() - w := b.Dx() - if img.Stride != w*4 { - panic("unsupported stride") - } - start := (b.Min.X + b.Min.Y*w) * 4 - end := (b.Max.X + (b.Max.Y-1)*w) * 4 - pixels := img.Pix[start:end] - res := (*_ID3D11Resource)(unsafe.Pointer(t.tex)) - t.backend.dev.ctx.UpdateSubresource(res, uint32(img.Stride), uint32(len(pixels)), pixels) -} - -func (t *Texture) Release() { - _IUnknownRelease(unsafe.Pointer(t.tex), t.tex.vtbl.Release) - t.tex = nil - if t.sampler != nil { - _IUnknownRelease(unsafe.Pointer(t.sampler), t.sampler.vtbl.Release) - t.sampler = nil - } - if t.resView != nil { - _IUnknownRelease(unsafe.Pointer(t.resView), t.resView.vtbl.Release) - t.resView = nil - } -} - -func (b *Backend) BindTexture(unit int, tex backend.Texture) { - t := tex.(*Texture) - b.dev.ctx.PSSetSamplers(uint32(unit), t.sampler) - b.dev.ctx.PSSetShaderResources(uint32(unit), t.resView) -} - -func (b *Backend) BindProgram(prog backend.Program) { - b.prog = prog.(*Program) -} - -func (p *Program) Release() { - _IUnknownRelease(unsafe.Pointer(p.vert.shader), p.vert.shader.vtbl.Release) - _IUnknownRelease(unsafe.Pointer(p.frag.shader), p.frag.shader.vtbl.Release) - p.vert.shader = nil - p.frag.shader = nil -} - -func (p *Program) SetVertexUniforms(buf backend.Buffer) { - p.vert.uniforms = buf.(*Buffer) -} - -func (p *Program) SetFragmentUniforms(buf backend.Buffer) { - p.frag.uniforms = buf.(*Buffer) -} - -func (b *Backend) BindVertexBuffer(buf backend.Buffer, stride, offset int) { - b.dev.ctx.IASetVertexBuffers(buf.(*Buffer).buf, uint32(stride), uint32(offset)) -} - -func (b *Backend) BindIndexBuffer(buf backend.Buffer) { - b.dev.ctx.IASetIndexBuffer(buf.(*Buffer).buf, _DXGI_FORMAT_R16_UINT, 0) -} - -func (b *Buffer) Upload(data []byte) { - b.backend.dev.ctx.UpdateSubresource((*_ID3D11Resource)(unsafe.Pointer(b.buf)), 0, 0, data) -} - -func (b *Buffer) Release() { - _IUnknownRelease(unsafe.Pointer(b.buf), b.buf.vtbl.Release) - b.buf = nil -} - -func (f *Framebuffer) ReadPixels(src image.Rectangle, pixels []byte) error { - if f.resource == nil { - return errors.New("framebuffer does not support ReadPixels") - } - w, h := src.Dx(), src.Dy() - tex, err := f.dev.dev.CreateTexture2D(&_D3D11_TEXTURE2D_DESC{ - Width: uint32(w), - Height: uint32(h), - MipLevels: 1, - ArraySize: 1, - Format: f.format, - SampleDesc: _DXGI_SAMPLE_DESC{ - Count: 1, - Quality: 0, - }, - Usage: _D3D11_USAGE_STAGING, - CPUAccessFlags: _D3D11_CPU_ACCESS_READ, - }) - if err != nil { - return fmt.Errorf("ReadPixels: %v", err) - } - defer _IUnknownRelease(unsafe.Pointer(tex), tex.vtbl.Release) - res := (*_ID3D11Resource)(unsafe.Pointer(tex)) - f.dev.ctx.CopySubresourceRegion( - res, - 0, // Destination subresource. - 0, 0, 0, // Destination coordinates (x, y, z). - f.resource, - 0, // Source subresource. - &_D3D11_BOX{ - left: uint32(src.Min.X), - top: uint32(src.Min.Y), - right: uint32(src.Max.X), - bottom: uint32(src.Max.Y), - front: 0, - back: 1, - }, - ) - resMap, err := f.dev.ctx.Map(res, 0, _D3D11_MAP_READ, 0) - if err != nil { - return fmt.Errorf("ReadPixels: %v", err) - } - defer f.dev.ctx.Unmap(res, 0) - srcPitch := w * 4 - dstPitch := int(resMap.RowPitch) - mapSize := dstPitch * h - data := gunsafe.SliceOf(resMap.pData)[:mapSize:mapSize] - width := w * 4 - for r := 0; r < h; r++ { - pixels := pixels[r*srcPitch:] - copy(pixels[:width], data[r*dstPitch:]) - } - return nil -} - -func (b *Backend) BindFramebuffer(fbo backend.Framebuffer) { - b.fbo = fbo.(*Framebuffer) - b.dev.ctx.OMSetRenderTargets(b.fbo.renderTarget, b.fbo.depthView) -} - -func (f *Framebuffer) Invalidate() { -} - -func (f *Framebuffer) Release() { - if f.foreign { - panic("cannot release Framebuffer from CurrentFramebuffer") - } - if f.renderTarget != nil { - _IUnknownRelease(unsafe.Pointer(f.renderTarget), f.renderTarget.vtbl.Release) - f.renderTarget = nil - } - if f.depthView != nil { - _IUnknownRelease(unsafe.Pointer(f.depthView), f.depthView.vtbl.Release) - f.depthView = nil - } -} - -func (b *Backend) BindInputLayout(layout backend.InputLayout) { - b.dev.ctx.IASetInputLayout(layout.(*InputLayout).layout) -} - -func (l *InputLayout) Release() { - _IUnknownRelease(unsafe.Pointer(l.layout), l.layout.vtbl.Release) - l.layout = nil -} - -func convBufferBinding(typ backend.BufferBinding) uint32 { - var bindings uint32 - if typ&backend.BufferBindingVertices != 0 { - bindings |= _D3D11_BIND_VERTEX_BUFFER - } - if typ&backend.BufferBindingIndices != 0 { - bindings |= _D3D11_BIND_INDEX_BUFFER - } - if typ&backend.BufferBindingUniforms != 0 { - bindings |= _D3D11_BIND_CONSTANT_BUFFER - } - if typ&backend.BufferBindingTexture != 0 { - bindings |= _D3D11_BIND_SHADER_RESOURCE - } - if typ&backend.BufferBindingFramebuffer != 0 { - bindings |= _D3D11_BIND_RENDER_TARGET - } - return bindings -} - -func toBlendFactor(f backend.BlendFactor) (uint32, uint32) { - switch f { - case backend.BlendFactorOne: - return _D3D11_BLEND_ONE, _D3D11_BLEND_ONE - case backend.BlendFactorOneMinusSrcAlpha: - return _D3D11_BLEND_INV_SRC_ALPHA, _D3D11_BLEND_INV_SRC_ALPHA - case backend.BlendFactorZero: - return _D3D11_BLEND_ZERO, _D3D11_BLEND_ZERO - case backend.BlendFactorDstColor: - return _D3D11_BLEND_DEST_COLOR, _D3D11_BLEND_DEST_ALPHA - default: - panic("unsupported blend source factor") - } -} diff --git a/vendor/gioui.org/app/internal/d3d11/d3d11_windows.go b/vendor/gioui.org/app/internal/d3d11/d3d11_windows.go deleted file mode 100644 index 3dd2138..0000000 --- a/vendor/gioui.org/app/internal/d3d11/d3d11_windows.go +++ /dev/null @@ -1,1344 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package d3d11 - -import ( - "fmt" - "math" - "syscall" - "unsafe" - - "gioui.org/internal/f32color" - - "golang.org/x/sys/windows" -) - -type _DXGI_SWAP_CHAIN_DESC struct { - BufferDesc _DXGI_MODE_DESC - SampleDesc _DXGI_SAMPLE_DESC - BufferUsage uint32 - BufferCount uint32 - OutputWindow windows.Handle - Windowed uint32 - SwapEffect uint32 - Flags uint32 -} - -type _DXGI_SAMPLE_DESC struct { - Count uint32 - Quality uint32 -} - -type _DXGI_MODE_DESC struct { - Width uint32 - Height uint32 - RefreshRate _DXGI_RATIONAL - Format uint32 - ScanlineOrdering uint32 - Scaling uint32 -} - -type _DXGI_RATIONAL struct { - Numerator uint32 - Denominator uint32 -} - -type _D3D11_TEXTURE2D_DESC struct { - Width uint32 - Height uint32 - MipLevels uint32 - ArraySize uint32 - Format uint32 - SampleDesc _DXGI_SAMPLE_DESC - Usage uint32 - BindFlags uint32 - CPUAccessFlags uint32 - MiscFlags uint32 -} - -type _D3D11_SAMPLER_DESC struct { - Filter uint32 - AddressU uint32 - AddressV uint32 - AddressW uint32 - MipLODBias float32 - MaxAnisotropy uint32 - ComparisonFunc uint32 - BorderColor [4]float32 - MinLOD float32 - MaxLOD float32 -} - -type _D3D11_SHADER_RESOURCE_VIEW_DESC_TEX2D struct { - _D3D11_SHADER_RESOURCE_VIEW_DESC - Texture2D _D3D11_TEX2D_SRV -} - -type _D3D11_SHADER_RESOURCE_VIEW_DESC struct { - Format uint32 - ViewDimension uint32 -} - -type _D3D11_TEX2D_SRV struct { - MostDetailedMip uint32 - MipLevels uint32 -} - -type _D3D11_INPUT_ELEMENT_DESC struct { - SemanticName *byte - SemanticIndex uint32 - Format uint32 - InputSlot uint32 - AlignedByteOffset uint32 - InputSlotClass uint32 - InstanceDataStepRate uint32 -} - -type _IDXGISwapChain struct { - vtbl *struct { - _IUnknownVTbl - SetPrivateData uintptr - SetPrivateDataInterface uintptr - GetPrivateData uintptr - GetParent uintptr - GetDevice uintptr - Present uintptr - GetBuffer uintptr - SetFullscreenState uintptr - GetFullscreenState uintptr - GetDesc uintptr - ResizeBuffers uintptr - ResizeTarget uintptr - GetContainingOutput uintptr - GetFrameStatistics uintptr - GetLastPresentCount uintptr - } -} - -type _ID3D11Device struct { - vtbl *struct { - _IUnknownVTbl - CreateBuffer uintptr - CreateTexture1D uintptr - CreateTexture2D uintptr - CreateTexture3D uintptr - CreateShaderResourceView uintptr - CreateUnorderedAccessView uintptr - CreateRenderTargetView uintptr - CreateDepthStencilView uintptr - CreateInputLayout uintptr - CreateVertexShader uintptr - CreateGeometryShader uintptr - CreateGeometryShaderWithStreamOutput uintptr - CreatePixelShader uintptr - CreateHullShader uintptr - CreateDomainShader uintptr - CreateComputeShader uintptr - CreateClassLinkage uintptr - CreateBlendState uintptr - CreateDepthStencilState uintptr - CreateRasterizerState uintptr - CreateSamplerState uintptr - CreateQuery uintptr - CreatePredicate uintptr - CreateCounter uintptr - CreateDeferredContext uintptr - OpenSharedResource uintptr - CheckFormatSupport uintptr - CheckMultisampleQualityLevels uintptr - CheckCounterInfo uintptr - CheckCounter uintptr - CheckFeatureSupport uintptr - GetPrivateData uintptr - SetPrivateData uintptr - SetPrivateDataInterface uintptr - GetFeatureLevel uintptr - GetCreationFlags uintptr - GetDeviceRemovedReason uintptr - GetImmediateContext uintptr - SetExceptionMode uintptr - GetExceptionMode uintptr - } -} - -type _ID3D11DeviceContext struct { - vtbl *struct { - _IUnknownVTbl - GetDevice uintptr - GetPrivateData uintptr - SetPrivateData uintptr - SetPrivateDataInterface uintptr - VSSetConstantBuffers uintptr - PSSetShaderResources uintptr - PSSetShader uintptr - PSSetSamplers uintptr - VSSetShader uintptr - DrawIndexed uintptr - Draw uintptr - Map uintptr - Unmap uintptr - PSSetConstantBuffers uintptr - IASetInputLayout uintptr - IASetVertexBuffers uintptr - IASetIndexBuffer uintptr - DrawIndexedInstanced uintptr - DrawInstanced uintptr - GSSetConstantBuffers uintptr - GSSetShader uintptr - IASetPrimitiveTopology uintptr - VSSetShaderResources uintptr - VSSetSamplers uintptr - Begin uintptr - End uintptr - GetData uintptr - SetPredication uintptr - GSSetShaderResources uintptr - GSSetSamplers uintptr - OMSetRenderTargets uintptr - OMSetRenderTargetsAndUnorderedAccessViews uintptr - OMSetBlendState uintptr - OMSetDepthStencilState uintptr - SOSetTargets uintptr - DrawAuto uintptr - DrawIndexedInstancedIndirect uintptr - DrawInstancedIndirect uintptr - Dispatch uintptr - DispatchIndirect uintptr - RSSetState uintptr - RSSetViewports uintptr - RSSetScissorRects uintptr - CopySubresourceRegion uintptr - CopyResource uintptr - UpdateSubresource uintptr - CopyStructureCount uintptr - ClearRenderTargetView uintptr - ClearUnorderedAccessViewUint uintptr - ClearUnorderedAccessViewFloat uintptr - ClearDepthStencilView uintptr - GenerateMips uintptr - SetResourceMinLOD uintptr - GetResourceMinLOD uintptr - ResolveSubresource uintptr - ExecuteCommandList uintptr - HSSetShaderResources uintptr - HSSetShader uintptr - HSSetSamplers uintptr - HSSetConstantBuffers uintptr - DSSetShaderResources uintptr - DSSetShader uintptr - DSSetSamplers uintptr - DSSetConstantBuffers uintptr - CSSetShaderResources uintptr - CSSetUnorderedAccessViews uintptr - CSSetShader uintptr - CSSetSamplers uintptr - CSSetConstantBuffers uintptr - VSGetConstantBuffers uintptr - PSGetShaderResources uintptr - PSGetShader uintptr - PSGetSamplers uintptr - VSGetShader uintptr - PSGetConstantBuffers uintptr - IAGetInputLayout uintptr - IAGetVertexBuffers uintptr - IAGetIndexBuffer uintptr - GSGetConstantBuffers uintptr - GSGetShader uintptr - IAGetPrimitiveTopology uintptr - VSGetShaderResources uintptr - VSGetSamplers uintptr - GetPredication uintptr - GSGetShaderResources uintptr - GSGetSamplers uintptr - OMGetRenderTargets uintptr - OMGetRenderTargetsAndUnorderedAccessViews uintptr - OMGetBlendState uintptr - OMGetDepthStencilState uintptr - SOGetTargets uintptr - RSGetState uintptr - RSGetViewports uintptr - RSGetScissorRects uintptr - HSGetShaderResources uintptr - HSGetShader uintptr - HSGetSamplers uintptr - HSGetConstantBuffers uintptr - DSGetShaderResources uintptr - DSGetShader uintptr - DSGetSamplers uintptr - DSGetConstantBuffers uintptr - CSGetShaderResources uintptr - CSGetUnorderedAccessViews uintptr - CSGetShader uintptr - CSGetSamplers uintptr - CSGetConstantBuffers uintptr - ClearState uintptr - Flush uintptr - GetType uintptr - GetContextFlags uintptr - FinishCommandList uintptr - } -} - -type _ID3D11RenderTargetView struct { - vtbl *struct { - _IUnknownVTbl - } -} - -type _ID3D11Resource struct { - vtbl *struct { - _IUnknownVTbl - } -} - -type _ID3D11Texture2D struct { - vtbl *struct { - _IUnknownVTbl - } -} - -type _ID3D11Buffer struct { - vtbl *struct { - _IUnknownVTbl - } -} - -type _ID3D11SamplerState struct { - vtbl *struct { - _IUnknownVTbl - } -} - -type _ID3D11PixelShader struct { - vtbl *struct { - _IUnknownVTbl - } -} - -type _ID3D11ShaderResourceView struct { - vtbl *struct { - _IUnknownVTbl - } -} - -type _ID3D11DepthStencilView struct { - vtbl *struct { - _IUnknownVTbl - } -} - -type _ID3D11BlendState struct { - vtbl *struct { - _IUnknownVTbl - } -} - -type _ID3D11DepthStencilState struct { - vtbl *struct { - _IUnknownVTbl - } -} - -type _ID3D11VertexShader struct { - vtbl *struct { - _IUnknownVTbl - } -} - -type _ID3D11RasterizerState struct { - vtbl *struct { - _IUnknownVTbl - } -} - -type _ID3D11InputLayout struct { - vtbl *struct { - _IUnknownVTbl - GetBufferPointer uintptr - GetBufferSize uintptr - } -} - -type _D3D11_DEPTH_STENCIL_DESC struct { - DepthEnable uint32 - DepthWriteMask uint32 - DepthFunc uint32 - StencilEnable uint32 - StencilReadMask uint8 - StencilWriteMask uint8 - FrontFace _D3D11_DEPTH_STENCILOP_DESC - BackFace _D3D11_DEPTH_STENCILOP_DESC -} - -type _D3D11_DEPTH_STENCILOP_DESC struct { - StencilFailOp uint32 - StencilDepthFailOp uint32 - StencilPassOp uint32 - StencilFunc uint32 -} - -type _D3D11_DEPTH_STENCIL_VIEW_DESC_TEX2D struct { - Format uint32 - ViewDimension uint32 - Flags uint32 - Texture2D _D3D11_TEX2D_DSV -} - -type _D3D11_TEX2D_DSV struct { - MipSlice uint32 -} - -type _D3D11_BLEND_DESC struct { - AlphaToCoverageEnable uint32 - IndependentBlendEnable uint32 - RenderTarget [8]_D3D11_RENDER_TARGET_BLEND_DESC -} - -type _D3D11_RENDER_TARGET_BLEND_DESC struct { - BlendEnable uint32 - SrcBlend uint32 - DestBlend uint32 - BlendOp uint32 - SrcBlendAlpha uint32 - DestBlendAlpha uint32 - BlendOpAlpha uint32 - RenderTargetWriteMask uint8 -} - -type _IDXGIObject struct { - vtbl *struct { - _IUnknownVTbl - SetPrivateData uintptr - SetPrivateDataInterface uintptr - GetPrivateData uintptr - GetParent uintptr - } -} - -type _IDXGIAdapter struct { - vtbl *struct { - _IUnknownVTbl - SetPrivateData uintptr - SetPrivateDataInterface uintptr - GetPrivateData uintptr - GetParent uintptr - EnumOutputs uintptr - GetDesc uintptr - CheckInterfaceSupport uintptr - GetDesc1 uintptr - } -} - -type _IDXGIFactory struct { - vtbl *struct { - _IUnknownVTbl - SetPrivateData uintptr - SetPrivateDataInterface uintptr - GetPrivateData uintptr - GetParent uintptr - EnumAdapters uintptr - MakeWindowAssociation uintptr - GetWindowAssociation uintptr - CreateSwapChain uintptr - CreateSoftwareAdapter uintptr - } -} - -type _IDXGIDevice struct { - vtbl *struct { - _IUnknownVTbl - SetPrivateData uintptr - SetPrivateDataInterface uintptr - GetPrivateData uintptr - GetParent uintptr - GetAdapter uintptr - CreateSurface uintptr - QueryResourceResidency uintptr - SetGPUThreadPriority uintptr - GetGPUThreadPriority uintptr - } -} - -type _IUnknown struct { - vtbl *struct { - _IUnknownVTbl - } -} - -type _IUnknownVTbl struct { - QueryInterface uintptr - AddRef uintptr - Release uintptr -} - -type _D3D11_BUFFER_DESC struct { - ByteWidth uint32 - Usage uint32 - BindFlags uint32 - CPUAccessFlags uint32 - MiscFlags uint32 - StructureByteStride uint32 -} - -type _GUID struct { - Data1 uint32 - Data2 uint16 - Data3 uint16 - Data4_0 uint8 - Data4_1 uint8 - Data4_2 uint8 - Data4_3 uint8 - Data4_4 uint8 - Data4_5 uint8 - Data4_6 uint8 - Data4_7 uint8 -} - -type _D3D11_VIEWPORT struct { - TopLeftX float32 - TopLeftY float32 - Width float32 - Height float32 - MinDepth float32 - MaxDepth float32 -} - -type _D3D11_SUBRESOURCE_DATA struct { - pSysMem *byte -} - -type _D3D11_BOX struct { - left uint32 - top uint32 - front uint32 - right uint32 - bottom uint32 - back uint32 -} - -type _D3D11_MAPPED_SUBRESOURCE struct { - pData uintptr - RowPitch uint32 - DepthPitch uint32 -} - -type ErrorCode struct { - Name string - Code uint32 -} - -type _D3D11_RASTERIZER_DESC struct { - FillMode uint32 - CullMode uint32 - FrontCounterClockwise uint32 - DepthBias int32 - DepthBiasClamp float32 - SlopeScaledDepthBias float32 - DepthClipEnable uint32 - ScissorEnable uint32 - MultisampleEnable uint32 - AntialiasedLineEnable uint32 -} - -var ( - _IID_ID3D11Texture2D = _GUID{0x6f15aaf2, 0xd208, 0x4e89, 0x9a, 0xb4, 0x48, 0x95, 0x35, 0xd3, 0x4f, 0x9c} - _IID_IDXGIDevice = _GUID{0x54ec77fa, 0x1377, 0x44e6, 0x8c, 0x32, 0x88, 0xfd, 0x5f, 0x44, 0xc8, 0x4c} - _IID_IDXGIFactory = _GUID{0x7b7166ec, 0x21c7, 0x44ae, 0xb2, 0x1a, 0xc9, 0xae, 0x32, 0x1a, 0xe3, 0x69} -) - -var ( - d3d11 = windows.NewLazySystemDLL("d3d11.dll") - - __D3D11CreateDevice = d3d11.NewProc("D3D11CreateDevice") - __D3D11CreateDeviceAndSwapChain = d3d11.NewProc("D3D11CreateDeviceAndSwapChain") -) - -const ( - _D3D11_SDK_VERSION = 7 - _D3D_DRIVER_TYPE_HARDWARE = 1 - - _DXGI_FORMAT_UNKNOWN = 0 - _DXGI_FORMAT_R16_FLOAT = 54 - _DXGI_FORMAT_R32_FLOAT = 41 - _DXGI_FORMAT_R32G32_FLOAT = 16 - _DXGI_FORMAT_R32G32B32_FLOAT = 6 - _DXGI_FORMAT_R32G32B32A32_FLOAT = 2 - _DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29 - _DXGI_FORMAT_R16_SINT = 59 - _DXGI_FORMAT_R16G16_SINT = 38 - _DXGI_FORMAT_R16_UINT = 57 - _DXGI_FORMAT_D24_UNORM_S8_UINT = 45 - _DXGI_FORMAT_R16G16_FLOAT = 34 - _DXGI_FORMAT_R16G16B16A16_FLOAT = 10 - - _D3D11_FORMAT_SUPPORT_TEXTURE2D = 0x20 - _D3D11_FORMAT_SUPPORT_RENDER_TARGET = 0x4000 - - _DXGI_USAGE_RENDER_TARGET_OUTPUT = 1 << (1 + 4) - - _D3D11_CPU_ACCESS_READ = 0x20000 - - _D3D11_MAP_READ = 1 - - _DXGI_SWAP_EFFECT_DISCARD = 0 - - _D3D_FEATURE_LEVEL_9_1 = 0x9100 - _D3D_FEATURE_LEVEL_9_3 = 0x9300 - _D3D_FEATURE_LEVEL_11_0 = 0xb000 - - _D3D11_USAGE_IMMUTABLE = 1 - _D3D11_USAGE_STAGING = 3 - - _D3D11_BIND_VERTEX_BUFFER = 0x1 - _D3D11_BIND_INDEX_BUFFER = 0x2 - _D3D11_BIND_CONSTANT_BUFFER = 0x4 - _D3D11_BIND_SHADER_RESOURCE = 0x8 - _D3D11_BIND_RENDER_TARGET = 0x20 - _D3D11_BIND_DEPTH_STENCIL = 0x40 - - _D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST = 4 - _D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP = 5 - - _D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT = 0x14 - _D3D11_FILTER_MIN_MAG_MIP_POINT = 0 - - _D3D11_TEXTURE_ADDRESS_MIRROR = 2 - _D3D11_TEXTURE_ADDRESS_CLAMP = 3 - _D3D11_TEXTURE_ADDRESS_WRAP = 1 - - _D3D11_SRV_DIMENSION_TEXTURE2D = 4 - - _D3D11_CREATE_DEVICE_DEBUG = 0x2 - - _D3D11_FILL_SOLID = 3 - - _D3D11_CULL_NONE = 1 - - _D3D11_CLEAR_DEPTH = 0x1 - _D3D11_CLEAR_STENCIL = 0x2 - - _D3D11_DSV_DIMENSION_TEXTURE2D = 3 - - _D3D11_DEPTH_WRITE_MASK_ALL = 1 - - _D3D11_COMPARISON_GREATER = 5 - _D3D11_COMPARISON_GREATER_EQUAL = 7 - - _D3D11_BLEND_OP_ADD = 1 - _D3D11_BLEND_ONE = 2 - _D3D11_BLEND_INV_SRC_ALPHA = 6 - _D3D11_BLEND_ZERO = 1 - _D3D11_BLEND_DEST_COLOR = 9 - _D3D11_BLEND_DEST_ALPHA = 7 - - _D3D11_COLOR_WRITE_ENABLE_ALL = 1 | 2 | 4 | 8 - - DXGI_STATUS_OCCLUDED = 0x087A0001 - DXGI_ERROR_DEVICE_RESET = 0x887A0007 - DXGI_ERROR_DEVICE_REMOVED = 0x887A0005 - D3DDDIERR_DEVICEREMOVED = 1<<31 | 0x876<<16 | 2160 -) - -func _D3D11CreateDevice(driverType uint32, flags uint32) (*_ID3D11Device, *_ID3D11DeviceContext, uint32, error) { - var ( - dev *_ID3D11Device - ctx *_ID3D11DeviceContext - featLvl uint32 - ) - r, _, _ := __D3D11CreateDevice.Call( - 0, // pAdapter - uintptr(driverType), // driverType - 0, // Software - uintptr(flags), // Flags - 0, // pFeatureLevels - 0, // FeatureLevels - _D3D11_SDK_VERSION, // SDKVersion - uintptr(unsafe.Pointer(&dev)), // ppDevice - uintptr(unsafe.Pointer(&featLvl)), // pFeatureLevel - uintptr(unsafe.Pointer(&ctx)), // ppImmediateContext - ) - if r != 0 { - return nil, nil, 0, ErrorCode{Name: "D3D11CreateDevice", Code: uint32(r)} - } - return dev, ctx, featLvl, nil -} - -func _D3D11CreateDeviceAndSwapChain(driverType uint32, flags uint32, swapDesc *_DXGI_SWAP_CHAIN_DESC) (*_ID3D11Device, *_ID3D11DeviceContext, *_IDXGISwapChain, uint32, error) { - var ( - dev *_ID3D11Device - ctx *_ID3D11DeviceContext - swchain *_IDXGISwapChain - featLvl uint32 - ) - r, _, _ := __D3D11CreateDeviceAndSwapChain.Call( - 0, // pAdapter - uintptr(driverType), // driverType - 0, // Software - uintptr(flags), // Flags - 0, // pFeatureLevels - 0, // FeatureLevels - _D3D11_SDK_VERSION, // SDKVersion - uintptr(unsafe.Pointer(swapDesc)), // pSwapChainDesc - uintptr(unsafe.Pointer(&swchain)), // ppSwapChain - uintptr(unsafe.Pointer(&dev)), // ppDevice - uintptr(unsafe.Pointer(&featLvl)), // pFeatureLevel - uintptr(unsafe.Pointer(&ctx)), // ppImmediateContext - ) - if r != 0 { - return nil, nil, nil, 0, ErrorCode{Name: "D3D11CreateDeviceAndSwapChain", Code: uint32(r)} - } - return dev, ctx, swchain, featLvl, nil -} - -func (d *_ID3D11Device) CheckFormatSupport(format uint32) (uint32, error) { - var support uint32 - r, _, _ := syscall.Syscall( - d.vtbl.CheckFormatSupport, - 3, - uintptr(unsafe.Pointer(d)), - uintptr(format), - uintptr(unsafe.Pointer(&support)), - ) - if r != 0 { - return 0, ErrorCode{Name: "ID3D11DeviceCheckFormatSupport", Code: uint32(r)} - } - return support, nil -} - -func (d *_ID3D11Device) CreateBuffer(desc *_D3D11_BUFFER_DESC, data []byte) (*_ID3D11Buffer, error) { - var dataDesc *_D3D11_SUBRESOURCE_DATA - if len(data) > 0 { - dataDesc = &_D3D11_SUBRESOURCE_DATA{ - pSysMem: &data[0], - } - } - var buf *_ID3D11Buffer - r, _, _ := syscall.Syscall6( - d.vtbl.CreateBuffer, - 4, - uintptr(unsafe.Pointer(d)), - uintptr(unsafe.Pointer(desc)), - uintptr(unsafe.Pointer(dataDesc)), - uintptr(unsafe.Pointer(&buf)), - 0, 0, - ) - if r != 0 { - return nil, ErrorCode{Name: "ID3D11DeviceCreateBuffer", Code: uint32(r)} - } - return buf, nil -} - -func (d *_ID3D11Device) CreateDepthStencilViewTEX2D(res *_ID3D11Resource, desc *_D3D11_DEPTH_STENCIL_VIEW_DESC_TEX2D) (*_ID3D11DepthStencilView, error) { - var view *_ID3D11DepthStencilView - r, _, _ := syscall.Syscall6( - d.vtbl.CreateDepthStencilView, - 4, - uintptr(unsafe.Pointer(d)), - uintptr(unsafe.Pointer(res)), - uintptr(unsafe.Pointer(desc)), - uintptr(unsafe.Pointer(&view)), - 0, 0, - ) - if r != 0 { - return nil, ErrorCode{Name: "ID3D11DeviceCreateDepthStencilView", Code: uint32(r)} - } - return view, nil -} - -func (d *_ID3D11Device) CreatePixelShader(bytecode []byte) (*_ID3D11PixelShader, error) { - var shader *_ID3D11PixelShader - r, _, _ := syscall.Syscall6( - d.vtbl.CreatePixelShader, - 5, - uintptr(unsafe.Pointer(d)), - uintptr(unsafe.Pointer(&bytecode[0])), - uintptr(len(bytecode)), - 0, // pClassLinkage - uintptr(unsafe.Pointer(&shader)), - 0, - ) - if r != 0 { - return nil, ErrorCode{Name: "ID3D11DeviceCreatePixelShader", Code: uint32(r)} - } - return shader, nil -} - -func (d *_ID3D11Device) CreateVertexShader(bytecode []byte) (*_ID3D11VertexShader, error) { - var shader *_ID3D11VertexShader - r, _, _ := syscall.Syscall6( - d.vtbl.CreateVertexShader, - 5, - uintptr(unsafe.Pointer(d)), - uintptr(unsafe.Pointer(&bytecode[0])), - uintptr(len(bytecode)), - 0, // pClassLinkage - uintptr(unsafe.Pointer(&shader)), - 0, - ) - if r != 0 { - return nil, ErrorCode{Name: "ID3D11DeviceCreateVertexShader", Code: uint32(r)} - } - return shader, nil -} - -func (d *_ID3D11Device) CreateShaderResourceViewTEX2D(res *_ID3D11Resource, desc *_D3D11_SHADER_RESOURCE_VIEW_DESC_TEX2D) (*_ID3D11ShaderResourceView, error) { - var resView *_ID3D11ShaderResourceView - r, _, _ := syscall.Syscall6( - d.vtbl.CreateShaderResourceView, - 4, - uintptr(unsafe.Pointer(d)), - uintptr(unsafe.Pointer(res)), - uintptr(unsafe.Pointer(desc)), - uintptr(unsafe.Pointer(&resView)), - 0, 0, - ) - if r != 0 { - return nil, ErrorCode{Name: "ID3D11DeviceCreateShaderResourceView", Code: uint32(r)} - } - return resView, nil -} - -func (d *_ID3D11Device) CreateRasterizerState(desc *_D3D11_RASTERIZER_DESC) (*_ID3D11RasterizerState, error) { - var state *_ID3D11RasterizerState - r, _, _ := syscall.Syscall( - d.vtbl.CreateRasterizerState, - 3, - uintptr(unsafe.Pointer(d)), - uintptr(unsafe.Pointer(desc)), - uintptr(unsafe.Pointer(&state)), - ) - if r != 0 { - return nil, ErrorCode{Name: "ID3D11DeviceCreateRasterizerState", Code: uint32(r)} - } - return state, nil -} - -func (d *_ID3D11Device) CreateInputLayout(descs []_D3D11_INPUT_ELEMENT_DESC, bytecode []byte) (*_ID3D11InputLayout, error) { - var pdesc *_D3D11_INPUT_ELEMENT_DESC - if len(descs) > 0 { - pdesc = &descs[0] - } - var layout *_ID3D11InputLayout - r, _, _ := syscall.Syscall6( - d.vtbl.CreateInputLayout, - 6, - uintptr(unsafe.Pointer(d)), - uintptr(unsafe.Pointer(pdesc)), - uintptr(len(descs)), - uintptr(unsafe.Pointer(&bytecode[0])), - uintptr(len(bytecode)), - uintptr(unsafe.Pointer(&layout)), - ) - if r != 0 { - return nil, ErrorCode{Name: "ID3D11DeviceCreateInputLayout", Code: uint32(r)} - } - return layout, nil -} - -func (d *_ID3D11Device) CreateSamplerState(desc *_D3D11_SAMPLER_DESC) (*_ID3D11SamplerState, error) { - var sampler *_ID3D11SamplerState - r, _, _ := syscall.Syscall( - d.vtbl.CreateSamplerState, - 3, - uintptr(unsafe.Pointer(d)), - uintptr(unsafe.Pointer(desc)), - uintptr(unsafe.Pointer(&sampler)), - ) - if r != 0 { - return nil, ErrorCode{Name: "ID3D11DeviceCreateSamplerState", Code: uint32(r)} - } - return sampler, nil -} - -func (d *_ID3D11Device) CreateTexture2D(desc *_D3D11_TEXTURE2D_DESC) (*_ID3D11Texture2D, error) { - var tex *_ID3D11Texture2D - r, _, _ := syscall.Syscall6( - d.vtbl.CreateTexture2D, - 4, - uintptr(unsafe.Pointer(d)), - uintptr(unsafe.Pointer(desc)), - 0, // pInitialData - uintptr(unsafe.Pointer(&tex)), - 0, 0, - ) - if r != 0 { - return nil, ErrorCode{Name: "ID3D11CreateTexture2D", Code: uint32(r)} - } - return tex, nil -} - -func (d *_ID3D11Device) CreateRenderTargetView(res *_ID3D11Resource) (*_ID3D11RenderTargetView, error) { - var target *_ID3D11RenderTargetView - r, _, _ := syscall.Syscall6( - d.vtbl.CreateRenderTargetView, - 4, - uintptr(unsafe.Pointer(d)), - uintptr(unsafe.Pointer(res)), - 0, // pDesc - uintptr(unsafe.Pointer(&target)), - 0, 0, - ) - if r != 0 { - return nil, ErrorCode{Name: "ID3D11DeviceCreateRenderTargetView", Code: uint32(r)} - } - return target, nil -} - -func (d *_ID3D11Device) CreateBlendState(desc *_D3D11_BLEND_DESC) (*_ID3D11BlendState, error) { - var state *_ID3D11BlendState - r, _, _ := syscall.Syscall( - d.vtbl.CreateBlendState, - 3, - uintptr(unsafe.Pointer(d)), - uintptr(unsafe.Pointer(desc)), - uintptr(unsafe.Pointer(&state)), - ) - if r != 0 { - return nil, ErrorCode{Name: "ID3D11DeviceCreateBlendState", Code: uint32(r)} - } - return state, nil -} - -func (d *_ID3D11Device) CreateDepthStencilState(desc *_D3D11_DEPTH_STENCIL_DESC) (*_ID3D11DepthStencilState, error) { - var state *_ID3D11DepthStencilState - r, _, _ := syscall.Syscall( - d.vtbl.CreateDepthStencilState, - 3, - uintptr(unsafe.Pointer(d)), - uintptr(unsafe.Pointer(desc)), - uintptr(unsafe.Pointer(&state)), - ) - if r != 0 { - return nil, ErrorCode{Name: "ID3D11DeviceCreateDepthStencilState", Code: uint32(r)} - } - return state, nil -} - -func (s *_IDXGISwapChain) GetDesc() (_DXGI_SWAP_CHAIN_DESC, error) { - var desc _DXGI_SWAP_CHAIN_DESC - r, _, _ := syscall.Syscall( - s.vtbl.GetDesc, - 2, - uintptr(unsafe.Pointer(s)), - uintptr(unsafe.Pointer(&desc)), - 0, - ) - if r != 0 { - return _DXGI_SWAP_CHAIN_DESC{}, ErrorCode{Name: "IDXGISwapChainGetDesc", Code: uint32(r)} - } - return desc, nil -} - -func (s *_IDXGISwapChain) ResizeBuffers(buffers, width, height, newFormat, flags uint32) error { - r, _, _ := syscall.Syscall6( - s.vtbl.ResizeBuffers, - 6, - uintptr(unsafe.Pointer(s)), - uintptr(buffers), - uintptr(width), - uintptr(height), - uintptr(newFormat), - uintptr(flags), - ) - if r != 0 { - return ErrorCode{Name: "IDXGISwapChainResizeBuffers", Code: uint32(r)} - } - return nil -} - -func (s *_IDXGISwapChain) Present(SyncInterval int, Flags uint32) error { - r, _, _ := syscall.Syscall( - s.vtbl.Present, - 3, - uintptr(unsafe.Pointer(s)), - uintptr(SyncInterval), - uintptr(Flags), - ) - if r != 0 { - return ErrorCode{Name: "IDXGISwapChainPresent", Code: uint32(r)} - } - return nil -} - -func (s *_IDXGISwapChain) GetBuffer(index int, riid *_GUID) (*_IUnknown, error) { - var buf *_IUnknown - r, _, _ := syscall.Syscall6( - s.vtbl.GetBuffer, - 4, - uintptr(unsafe.Pointer(s)), - uintptr(index), - uintptr(unsafe.Pointer(riid)), - uintptr(unsafe.Pointer(&buf)), - 0, - 0, - ) - if r != 0 { - return nil, ErrorCode{Name: "IDXGISwapChainGetBuffer", Code: uint32(r)} - } - return buf, nil -} - -func (c *_ID3D11DeviceContext) Unmap(resource *_ID3D11Resource, subResource uint32) { - syscall.Syscall( - c.vtbl.Unmap, - 3, - uintptr(unsafe.Pointer(c)), - uintptr(unsafe.Pointer(resource)), - uintptr(subResource), - ) -} - -func (c *_ID3D11DeviceContext) Map(resource *_ID3D11Resource, subResource, mapType, mapFlags uint32) (_D3D11_MAPPED_SUBRESOURCE, error) { - var resMap _D3D11_MAPPED_SUBRESOURCE - r, _, _ := syscall.Syscall6( - c.vtbl.Map, - 6, - uintptr(unsafe.Pointer(c)), - uintptr(unsafe.Pointer(resource)), - uintptr(subResource), - uintptr(mapType), - uintptr(mapFlags), - uintptr(unsafe.Pointer(&resMap)), - ) - if r != 0 { - return resMap, ErrorCode{Name: "ID3D11DeviceContextMap", Code: uint32(r)} - } - return resMap, nil -} - -func (c *_ID3D11DeviceContext) CopySubresourceRegion(dst *_ID3D11Resource, dstSubresource, dstX, dstY, dstZ uint32, src *_ID3D11Resource, srcSubresource uint32, srcBox *_D3D11_BOX) { - syscall.Syscall9( - c.vtbl.CopySubresourceRegion, - 9, - uintptr(unsafe.Pointer(c)), - uintptr(unsafe.Pointer(dst)), - uintptr(dstSubresource), - uintptr(dstX), - uintptr(dstY), - uintptr(dstZ), - uintptr(unsafe.Pointer(src)), - uintptr(srcSubresource), - uintptr(unsafe.Pointer(srcBox)), - ) -} - -func (c *_ID3D11DeviceContext) ClearDepthStencilView(target *_ID3D11DepthStencilView, flags uint32, depth float32, stencil uint8) { - syscall.Syscall6( - c.vtbl.ClearDepthStencilView, - 5, - uintptr(unsafe.Pointer(c)), - uintptr(unsafe.Pointer(target)), - uintptr(flags), - uintptr(math.Float32bits(depth)), - uintptr(stencil), - 0, - ) -} - -func (c *_ID3D11DeviceContext) ClearRenderTargetView(target *_ID3D11RenderTargetView, color *[4]float32) { - syscall.Syscall( - c.vtbl.ClearRenderTargetView, - 3, - uintptr(unsafe.Pointer(c)), - uintptr(unsafe.Pointer(target)), - uintptr(unsafe.Pointer(color)), - ) -} - -func (c *_ID3D11DeviceContext) RSSetViewports(viewport *_D3D11_VIEWPORT) { - syscall.Syscall( - c.vtbl.RSSetViewports, - 3, - uintptr(unsafe.Pointer(c)), - 1, // NumViewports - uintptr(unsafe.Pointer(viewport)), - ) -} - -func (c *_ID3D11DeviceContext) VSSetShader(s *_ID3D11VertexShader) { - syscall.Syscall6( - c.vtbl.VSSetShader, - 4, - uintptr(unsafe.Pointer(c)), - uintptr(unsafe.Pointer(s)), - 0, // ppClassInstances - 0, // NumClassInstances - 0, 0, - ) -} - -func (c *_ID3D11DeviceContext) VSSetConstantBuffers(b *_ID3D11Buffer) { - syscall.Syscall6( - c.vtbl.VSSetConstantBuffers, - 4, - uintptr(unsafe.Pointer(c)), - 0, // StartSlot - 1, // NumBuffers - uintptr(unsafe.Pointer(&b)), - 0, 0, - ) -} - -func (c *_ID3D11DeviceContext) PSSetConstantBuffers(b *_ID3D11Buffer) { - syscall.Syscall6( - c.vtbl.PSSetConstantBuffers, - 4, - uintptr(unsafe.Pointer(c)), - 0, // StartSlot - 1, // NumBuffers - uintptr(unsafe.Pointer(&b)), - 0, 0, - ) -} - -func (c *_ID3D11DeviceContext) PSSetShaderResources(startSlot uint32, s *_ID3D11ShaderResourceView) { - syscall.Syscall6( - c.vtbl.PSSetShaderResources, - 4, - uintptr(unsafe.Pointer(c)), - uintptr(startSlot), - 1, // NumViews - uintptr(unsafe.Pointer(&s)), - 0, 0, - ) -} - -func (c *_ID3D11DeviceContext) PSSetSamplers(startSlot uint32, s *_ID3D11SamplerState) { - syscall.Syscall6( - c.vtbl.PSSetSamplers, - 4, - uintptr(unsafe.Pointer(c)), - uintptr(startSlot), - 1, // NumSamplers - uintptr(unsafe.Pointer(&s)), - 0, 0, - ) -} - -func (c *_ID3D11DeviceContext) PSSetShader(s *_ID3D11PixelShader) { - syscall.Syscall6( - c.vtbl.PSSetShader, - 4, - uintptr(unsafe.Pointer(c)), - uintptr(unsafe.Pointer(s)), - 0, // ppClassInstances - 0, // NumClassInstances - 0, 0, - ) -} - -func (c *_ID3D11DeviceContext) UpdateSubresource(res *_ID3D11Resource, rowPitch, depthPitch uint32, data []byte) { - syscall.Syscall9( - c.vtbl.UpdateSubresource, - 7, - uintptr(unsafe.Pointer(c)), - uintptr(unsafe.Pointer(res)), - 0, // DstSubresource - 0, // pDstBox - uintptr(unsafe.Pointer(&data[0])), - uintptr(rowPitch), - uintptr(depthPitch), - 0, 0, - ) -} - -func (c *_ID3D11DeviceContext) RSSetState(state *_ID3D11RasterizerState) { - syscall.Syscall( - c.vtbl.RSSetState, - 2, - uintptr(unsafe.Pointer(c)), - uintptr(unsafe.Pointer(state)), - 0, - ) -} - -func (c *_ID3D11DeviceContext) IASetInputLayout(layout *_ID3D11InputLayout) { - syscall.Syscall( - c.vtbl.IASetInputLayout, - 2, - uintptr(unsafe.Pointer(c)), - uintptr(unsafe.Pointer(layout)), - 0, - ) -} - -func (c *_ID3D11DeviceContext) IASetIndexBuffer(buf *_ID3D11Buffer, format, offset uint32) { - syscall.Syscall6( - c.vtbl.IASetIndexBuffer, - 4, - uintptr(unsafe.Pointer(c)), - uintptr(unsafe.Pointer(buf)), - uintptr(format), - uintptr(offset), - 0, 0, - ) -} - -func (c *_ID3D11DeviceContext) IASetVertexBuffers(buf *_ID3D11Buffer, stride, offset uint32) { - syscall.Syscall6( - c.vtbl.IASetVertexBuffers, - 6, - uintptr(unsafe.Pointer(c)), - 0, // StartSlot - 1, // NumBuffers, - uintptr(unsafe.Pointer(&buf)), - uintptr(unsafe.Pointer(&stride)), - uintptr(unsafe.Pointer(&offset)), - ) -} - -func (c *_ID3D11DeviceContext) IASetPrimitiveTopology(mode uint32) { - syscall.Syscall( - c.vtbl.IASetPrimitiveTopology, - 2, - uintptr(unsafe.Pointer(c)), - uintptr(mode), - 0, - ) -} - -func (c *_ID3D11DeviceContext) OMGetRenderTargets() *_ID3D11RenderTargetView { - var target *_ID3D11RenderTargetView - syscall.Syscall6( - c.vtbl.OMGetRenderTargets, - 4, - uintptr(unsafe.Pointer(c)), - 1, // NumViews - uintptr(unsafe.Pointer(&target)), - 0, // pDepthStencilView - 0, 0, - ) - return target -} - -func (c *_ID3D11DeviceContext) OMSetRenderTargets(target *_ID3D11RenderTargetView, depthStencil *_ID3D11DepthStencilView) { - syscall.Syscall6( - c.vtbl.OMSetRenderTargets, - 4, - uintptr(unsafe.Pointer(c)), - 1, // NumViews - uintptr(unsafe.Pointer(&target)), - uintptr(unsafe.Pointer(depthStencil)), - 0, 0, - ) -} - -func (c *_ID3D11DeviceContext) Draw(count, start uint32) { - syscall.Syscall( - c.vtbl.Draw, - 3, - uintptr(unsafe.Pointer(c)), - uintptr(count), - uintptr(start), - ) -} - -func (c *_ID3D11DeviceContext) DrawIndexed(count, start uint32, base int32) { - syscall.Syscall6( - c.vtbl.DrawIndexed, - 4, - uintptr(unsafe.Pointer(c)), - uintptr(count), - uintptr(start), - uintptr(base), - 0, 0, - ) -} - -func (c *_ID3D11DeviceContext) OMSetBlendState(state *_ID3D11BlendState, factor *f32color.RGBA, sampleMask uint32) { - syscall.Syscall6( - c.vtbl.OMSetBlendState, - 4, - uintptr(unsafe.Pointer(c)), - uintptr(unsafe.Pointer(state)), - uintptr(unsafe.Pointer(factor)), - uintptr(sampleMask), - 0, 0, - ) -} - -func (c *_ID3D11DeviceContext) OMSetDepthStencilState(state *_ID3D11DepthStencilState, stencilRef uint32) { - syscall.Syscall( - c.vtbl.OMSetDepthStencilState, - 3, - uintptr(unsafe.Pointer(c)), - uintptr(unsafe.Pointer(state)), - uintptr(stencilRef), - ) -} - -func (d *_IDXGIObject) GetParent(guid *_GUID) (*_IDXGIObject, error) { - var parent *_IDXGIObject - r, _, _ := syscall.Syscall( - d.vtbl.GetParent, - 3, - uintptr(unsafe.Pointer(d)), - uintptr(unsafe.Pointer(guid)), - uintptr(unsafe.Pointer(&parent)), - ) - if r != 0 { - return nil, ErrorCode{Name: "IDXGIObjectGetParent", Code: uint32(r)} - } - return parent, nil -} - -func (d *_IDXGIFactory) CreateSwapChain(device *_IUnknown, desc *_DXGI_SWAP_CHAIN_DESC) (*_IDXGISwapChain, error) { - var swchain *_IDXGISwapChain - r, _, _ := syscall.Syscall6( - d.vtbl.CreateSwapChain, - 4, - uintptr(unsafe.Pointer(d)), - uintptr(unsafe.Pointer(device)), - uintptr(unsafe.Pointer(desc)), - uintptr(unsafe.Pointer(&swchain)), - 0, 0, - ) - if r != 0 { - return nil, ErrorCode{Name: "IDXGIFactory", Code: uint32(r)} - } - return swchain, nil -} - -func (d *_IDXGIDevice) GetAdapter() (*_IDXGIAdapter, error) { - var adapter *_IDXGIAdapter - r, _, _ := syscall.Syscall( - d.vtbl.GetAdapter, - 2, - uintptr(unsafe.Pointer(d)), - uintptr(unsafe.Pointer(&adapter)), - 0, - ) - if r != 0 { - return nil, ErrorCode{Name: "IDXGIDeviceGetAdapter", Code: uint32(r)} - } - return adapter, nil -} - -func _IUnknownQueryInterface(obj unsafe.Pointer, queryInterfaceMethod uintptr, guid *_GUID) (*_IUnknown, error) { - var ref *_IUnknown - r, _, _ := syscall.Syscall( - queryInterfaceMethod, - 3, - uintptr(obj), - uintptr(unsafe.Pointer(guid)), - uintptr(unsafe.Pointer(&ref)), - ) - if r != 0 { - return nil, ErrorCode{Name: "IUnknownQueryInterface", Code: uint32(r)} - } - return ref, nil -} - -func _IUnknownRelease(obj unsafe.Pointer, releaseMethod uintptr) { - syscall.Syscall( - releaseMethod, - 1, - uintptr(obj), - 0, - 0, - ) -} - -func (e ErrorCode) Error() string { - return fmt.Sprintf("%s: %#x", e.Name, e.Code) -} diff --git a/vendor/gioui.org/app/internal/egl/egl.go b/vendor/gioui.org/app/internal/egl/egl.go deleted file mode 100644 index f1d2595..0000000 --- a/vendor/gioui.org/app/internal/egl/egl.go +++ /dev/null @@ -1,279 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -// +build linux windows freebsd openbsd - -package egl - -import ( - "errors" - "fmt" - "runtime" - "strings" - - "gioui.org/app/internal/glimpl" - "gioui.org/app/internal/srgb" - "gioui.org/gpu/backend" - "gioui.org/gpu/gl" -) - -type Context struct { - c *glimpl.Functions - disp _EGLDisplay - eglCtx *eglContext - eglSurf _EGLSurface - width, height int - refreshFBO bool - // For sRGB emulation. - srgbFBO *srgb.FBO -} - -type eglContext struct { - config _EGLConfig - ctx _EGLContext - visualID int - srgb bool - surfaceless bool -} - -var ( - nilEGLDisplay _EGLDisplay - nilEGLSurface _EGLSurface - nilEGLContext _EGLContext - nilEGLConfig _EGLConfig - EGL_DEFAULT_DISPLAY NativeDisplayType -) - -const ( - _EGL_ALPHA_SIZE = 0x3021 - _EGL_BLUE_SIZE = 0x3022 - _EGL_CONFIG_CAVEAT = 0x3027 - _EGL_CONTEXT_CLIENT_VERSION = 0x3098 - _EGL_DEPTH_SIZE = 0x3025 - _EGL_GL_COLORSPACE_KHR = 0x309d - _EGL_GL_COLORSPACE_SRGB_KHR = 0x3089 - _EGL_GREEN_SIZE = 0x3023 - _EGL_EXTENSIONS = 0x3055 - _EGL_NATIVE_VISUAL_ID = 0x302e - _EGL_NONE = 0x3038 - _EGL_OPENGL_ES2_BIT = 0x4 - _EGL_RED_SIZE = 0x3024 - _EGL_RENDERABLE_TYPE = 0x3040 - _EGL_SURFACE_TYPE = 0x3033 - _EGL_WINDOW_BIT = 0x4 -) - -func (c *Context) Release() { - if c.srgbFBO != nil { - c.srgbFBO.Release() - c.srgbFBO = nil - } - c.ReleaseSurface() - if c.eglCtx != nil { - eglDestroyContext(c.disp, c.eglCtx.ctx) - eglTerminate(c.disp) - eglReleaseThread() - c.eglCtx = nil - } - c.disp = nilEGLDisplay -} - -func (c *Context) Present() error { - if c.srgbFBO != nil { - c.srgbFBO.Blit() - } - if !eglSwapBuffers(c.disp, c.eglSurf) { - return fmt.Errorf("eglSwapBuffers failed (%x)", eglGetError()) - } - if c.srgbFBO != nil { - c.srgbFBO.AfterPresent() - } - return nil -} - -func NewContext(disp NativeDisplayType) (*Context, error) { - if err := loadEGL(); err != nil { - return nil, err - } - eglDisp := eglGetDisplay(disp) - // eglGetDisplay can return EGL_NO_DISPLAY yet no error - // (EGL_SUCCESS), in which case a default EGL display might be - // available. - if eglDisp == nilEGLDisplay { - eglDisp = eglGetDisplay(EGL_DEFAULT_DISPLAY) - } - if eglDisp == nilEGLDisplay { - return nil, fmt.Errorf("eglGetDisplay failed: 0x%x", eglGetError()) - } - eglCtx, err := createContext(eglDisp) - if err != nil { - return nil, err - } - c := &Context{ - disp: eglDisp, - eglCtx: eglCtx, - c: new(glimpl.Functions), - } - return c, nil -} - -func (c *Context) Functions() *glimpl.Functions { - return c.c -} - -func (c *Context) Backend() (backend.Device, error) { - return gl.NewBackend(c.c) -} - -func (c *Context) ReleaseSurface() { - if c.eglSurf == nilEGLSurface { - return - } - // Make sure any in-flight GL commands are complete. - c.c.Finish() - c.ReleaseCurrent() - eglDestroySurface(c.disp, c.eglSurf) - c.eglSurf = nilEGLSurface -} - -func (c *Context) VisualID() int { - return c.eglCtx.visualID -} - -func (c *Context) CreateSurface(win NativeWindowType, width, height int) error { - eglSurf, err := createSurface(c.disp, c.eglCtx, win) - c.eglSurf = eglSurf - c.width = width - c.height = height - c.refreshFBO = true - return err -} - -func (c *Context) ReleaseCurrent() { - if c.disp != nilEGLDisplay { - eglMakeCurrent(c.disp, nilEGLSurface, nilEGLSurface, nilEGLContext) - } -} - -func (c *Context) MakeCurrent() error { - if c.eglSurf == nilEGLSurface && !c.eglCtx.surfaceless { - return errors.New("no surface created yet EGL_KHR_surfaceless_context is not supported") - } - if !eglMakeCurrent(c.disp, c.eglSurf, c.eglSurf, c.eglCtx.ctx) { - return fmt.Errorf("eglMakeCurrent error 0x%x", eglGetError()) - } - if c.eglCtx.srgb || c.eglSurf == nilEGLSurface { - return nil - } - if c.srgbFBO == nil { - var err error - c.srgbFBO, err = srgb.New(c.c) - if err != nil { - return err - } - } - if c.refreshFBO { - c.refreshFBO = false - return c.srgbFBO.Refresh(c.width, c.height) - } - return nil -} - -func (c *Context) EnableVSync(enable bool) { - if enable { - eglSwapInterval(c.disp, 1) - } else { - eglSwapInterval(c.disp, 0) - } -} - -func hasExtension(exts []string, ext string) bool { - for _, e := range exts { - if ext == e { - return true - } - } - return false -} - -func createContext(disp _EGLDisplay) (*eglContext, error) { - major, minor, ret := eglInitialize(disp) - if !ret { - return nil, fmt.Errorf("eglInitialize failed: 0x%x", eglGetError()) - } - // sRGB framebuffer support on EGL 1.5 or if EGL_KHR_gl_colorspace is supported. - exts := strings.Split(eglQueryString(disp, _EGL_EXTENSIONS), " ") - srgb := major > 1 || minor >= 5 || hasExtension(exts, "EGL_KHR_gl_colorspace") - attribs := []_EGLint{ - _EGL_RENDERABLE_TYPE, _EGL_OPENGL_ES2_BIT, - _EGL_SURFACE_TYPE, _EGL_WINDOW_BIT, - _EGL_BLUE_SIZE, 8, - _EGL_GREEN_SIZE, 8, - _EGL_RED_SIZE, 8, - _EGL_CONFIG_CAVEAT, _EGL_NONE, - } - if srgb { - if runtime.GOOS == "linux" || runtime.GOOS == "android" { - // Some Mesa drivers crash if an sRGB framebuffer is requested without alpha. - // https://bugs.freedesktop.org/show_bug.cgi?id=107782. - // - // Also, some Android devices (Samsung S9) needs alpha for sRGB to work. - attribs = append(attribs, _EGL_ALPHA_SIZE, 1) - } - // Only request a depth buffer if we're going to render directly to the framebuffer. - attribs = append(attribs, _EGL_DEPTH_SIZE, 16) - } - attribs = append(attribs, _EGL_NONE) - eglCfg, ret := eglChooseConfig(disp, attribs) - if !ret { - return nil, fmt.Errorf("eglChooseConfig failed: 0x%x", eglGetError()) - } - if eglCfg == nilEGLConfig { - return nil, errors.New("eglChooseConfig returned 0 configs") - } - visID, ret := eglGetConfigAttrib(disp, eglCfg, _EGL_NATIVE_VISUAL_ID) - if !ret { - return nil, errors.New("newContext: eglGetConfigAttrib for _EGL_NATIVE_VISUAL_ID failed") - } - ctxAttribs := []_EGLint{ - _EGL_CONTEXT_CLIENT_VERSION, 3, - _EGL_NONE, - } - eglCtx := eglCreateContext(disp, eglCfg, nilEGLContext, ctxAttribs) - if eglCtx == nilEGLContext { - // Fall back to OpenGL ES 2 and rely on extensions. - ctxAttribs := []_EGLint{ - _EGL_CONTEXT_CLIENT_VERSION, 2, - _EGL_NONE, - } - eglCtx = eglCreateContext(disp, eglCfg, nilEGLContext, ctxAttribs) - if eglCtx == nilEGLContext { - return nil, fmt.Errorf("eglCreateContext failed: 0x%x", eglGetError()) - } - } - return &eglContext{ - config: _EGLConfig(eglCfg), - ctx: _EGLContext(eglCtx), - visualID: int(visID), - srgb: srgb, - surfaceless: hasExtension(exts, "EGL_KHR_surfaceless_context"), - }, nil -} - -func createSurface(disp _EGLDisplay, eglCtx *eglContext, win NativeWindowType) (_EGLSurface, error) { - var surfAttribs []_EGLint - if eglCtx.srgb { - surfAttribs = append(surfAttribs, _EGL_GL_COLORSPACE_KHR, _EGL_GL_COLORSPACE_SRGB_KHR) - } - surfAttribs = append(surfAttribs, _EGL_NONE) - eglSurf := eglCreateWindowSurface(disp, eglCtx.config, win, surfAttribs) - if eglSurf == nilEGLSurface && eglCtx.srgb { - // Try again without sRGB - eglCtx.srgb = false - surfAttribs = []_EGLint{_EGL_NONE} - eglSurf = eglCreateWindowSurface(disp, eglCtx.config, win, surfAttribs) - } - if eglSurf == nilEGLSurface { - return nilEGLSurface, fmt.Errorf("newContext: eglCreateWindowSurface failed 0x%x (sRGB=%v)", eglGetError(), eglCtx.srgb) - } - return eglSurf, nil -} diff --git a/vendor/gioui.org/app/internal/egl/egl_unix.go b/vendor/gioui.org/app/internal/egl/egl_unix.go deleted file mode 100644 index 0c6734f..0000000 --- a/vendor/gioui.org/app/internal/egl/egl_unix.go +++ /dev/null @@ -1,104 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -// +build linux freebsd openbsd - -package egl - -/* -#cgo linux,!android pkg-config: egl -#cgo freebsd openbsd android LDFLAGS: -lEGL -#cgo freebsd CFLAGS: -I/usr/local/include -#cgo freebsd LDFLAGS: -L/usr/local/lib -#cgo openbsd CFLAGS: -I/usr/X11R6/include -#cgo openbsd LDFLAGS: -L/usr/X11R6/lib -#cgo CFLAGS: -DMESA_EGL_NO_X11_HEADERS - -#include <EGL/egl.h> -#include <EGL/eglext.h> -*/ -import "C" - -type ( - _EGLint = C.EGLint - _EGLDisplay = C.EGLDisplay - _EGLConfig = C.EGLConfig - _EGLContext = C.EGLContext - _EGLSurface = C.EGLSurface - NativeDisplayType = C.EGLNativeDisplayType - NativeWindowType = C.EGLNativeWindowType -) - -func loadEGL() error { - return nil -} - -func eglChooseConfig(disp _EGLDisplay, attribs []_EGLint) (_EGLConfig, bool) { - var cfg C.EGLConfig - var ncfg C.EGLint - if C.eglChooseConfig(disp, &attribs[0], &cfg, 1, &ncfg) != C.EGL_TRUE { - return nilEGLConfig, false - } - return _EGLConfig(cfg), true -} - -func eglCreateContext(disp _EGLDisplay, cfg _EGLConfig, shareCtx _EGLContext, attribs []_EGLint) _EGLContext { - ctx := C.eglCreateContext(disp, cfg, shareCtx, &attribs[0]) - return _EGLContext(ctx) -} - -func eglDestroySurface(disp _EGLDisplay, surf _EGLSurface) bool { - return C.eglDestroySurface(disp, surf) == C.EGL_TRUE -} - -func eglDestroyContext(disp _EGLDisplay, ctx _EGLContext) bool { - return C.eglDestroyContext(disp, ctx) == C.EGL_TRUE -} - -func eglGetConfigAttrib(disp _EGLDisplay, cfg _EGLConfig, attr _EGLint) (_EGLint, bool) { - var val _EGLint - ret := C.eglGetConfigAttrib(disp, cfg, attr, &val) - return val, ret == C.EGL_TRUE -} - -func eglGetError() _EGLint { - return C.eglGetError() -} - -func eglInitialize(disp _EGLDisplay) (_EGLint, _EGLint, bool) { - var maj, min _EGLint - ret := C.eglInitialize(disp, &maj, &min) - return maj, min, ret == C.EGL_TRUE -} - -func eglMakeCurrent(disp _EGLDisplay, draw, read _EGLSurface, ctx _EGLContext) bool { - return C.eglMakeCurrent(disp, draw, read, ctx) == C.EGL_TRUE -} - -func eglReleaseThread() bool { - return C.eglReleaseThread() == C.EGL_TRUE -} - -func eglSwapBuffers(disp _EGLDisplay, surf _EGLSurface) bool { - return C.eglSwapBuffers(disp, surf) == C.EGL_TRUE -} - -func eglSwapInterval(disp _EGLDisplay, interval _EGLint) bool { - return C.eglSwapInterval(disp, interval) == C.EGL_TRUE -} - -func eglTerminate(disp _EGLDisplay) bool { - return C.eglTerminate(disp) == C.EGL_TRUE -} - -func eglQueryString(disp _EGLDisplay, name _EGLint) string { - return C.GoString(C.eglQueryString(disp, name)) -} - -func eglGetDisplay(disp NativeDisplayType) _EGLDisplay { - return C.eglGetDisplay(disp) -} - -func eglCreateWindowSurface(disp _EGLDisplay, conf _EGLConfig, win NativeWindowType, attribs []_EGLint) _EGLSurface { - eglSurf := C.eglCreateWindowSurface(disp, conf, win, &attribs[0]) - return eglSurf -} diff --git a/vendor/gioui.org/app/internal/egl/egl_windows.go b/vendor/gioui.org/app/internal/egl/egl_windows.go deleted file mode 100644 index 5d9a4fe..0000000 --- a/vendor/gioui.org/app/internal/egl/egl_windows.go +++ /dev/null @@ -1,164 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package egl - -import ( - "fmt" - "runtime" - "sync" - "unsafe" - - syscall "golang.org/x/sys/windows" - - "gioui.org/app/internal/glimpl" - gunsafe "gioui.org/internal/unsafe" -) - -type ( - _EGLint int32 - _EGLDisplay uintptr - _EGLConfig uintptr - _EGLContext uintptr - _EGLSurface uintptr - NativeDisplayType uintptr - NativeWindowType uintptr -) - -var ( - libEGL = syscall.NewLazyDLL("libEGL.dll") - _eglChooseConfig = libEGL.NewProc("eglChooseConfig") - _eglCreateContext = libEGL.NewProc("eglCreateContext") - _eglCreateWindowSurface = libEGL.NewProc("eglCreateWindowSurface") - _eglDestroyContext = libEGL.NewProc("eglDestroyContext") - _eglDestroySurface = libEGL.NewProc("eglDestroySurface") - _eglGetConfigAttrib = libEGL.NewProc("eglGetConfigAttrib") - _eglGetDisplay = libEGL.NewProc("eglGetDisplay") - _eglGetError = libEGL.NewProc("eglGetError") - _eglInitialize = libEGL.NewProc("eglInitialize") - _eglMakeCurrent = libEGL.NewProc("eglMakeCurrent") - _eglReleaseThread = libEGL.NewProc("eglReleaseThread") - _eglSwapInterval = libEGL.NewProc("eglSwapInterval") - _eglSwapBuffers = libEGL.NewProc("eglSwapBuffers") - _eglTerminate = libEGL.NewProc("eglTerminate") - _eglQueryString = libEGL.NewProc("eglQueryString") -) - -var loadOnce sync.Once - -func loadEGL() error { - var err error - loadOnce.Do(func() { - err = loadDLLs() - }) - return err -} - -func loadDLLs() error { - if err := loadDLL(libEGL, "libEGL.dll"); err != nil { - return err - } - if err := loadDLL(glimpl.LibGLESv2, "libGLESv2.dll"); err != nil { - return err - } - // d3dcompiler_47.dll is needed internally for shader compilation to function. - return loadDLL(syscall.NewLazyDLL("d3dcompiler_47.dll"), "d3dcompiler_47.dll") -} - -func loadDLL(dll *syscall.LazyDLL, name string) error { - err := dll.Load() - if err != nil { - return fmt.Errorf("egl: failed to load %s: %v", name, err) - } - return nil -} - -func eglChooseConfig(disp _EGLDisplay, attribs []_EGLint) (_EGLConfig, bool) { - var cfg _EGLConfig - var ncfg _EGLint - a := &attribs[0] - r, _, _ := _eglChooseConfig.Call(uintptr(disp), uintptr(unsafe.Pointer(a)), uintptr(unsafe.Pointer(&cfg)), 1, uintptr(unsafe.Pointer(&ncfg))) - issue34474KeepAlive(a) - return cfg, r != 0 -} - -func eglCreateContext(disp _EGLDisplay, cfg _EGLConfig, shareCtx _EGLContext, attribs []_EGLint) _EGLContext { - a := &attribs[0] - c, _, _ := _eglCreateContext.Call(uintptr(disp), uintptr(cfg), uintptr(shareCtx), uintptr(unsafe.Pointer(a))) - issue34474KeepAlive(a) - return _EGLContext(c) -} - -func eglCreateWindowSurface(disp _EGLDisplay, cfg _EGLConfig, win NativeWindowType, attribs []_EGLint) _EGLSurface { - a := &attribs[0] - s, _, _ := _eglCreateWindowSurface.Call(uintptr(disp), uintptr(cfg), uintptr(win), uintptr(unsafe.Pointer(a))) - issue34474KeepAlive(a) - return _EGLSurface(s) -} - -func eglDestroySurface(disp _EGLDisplay, surf _EGLSurface) bool { - r, _, _ := _eglDestroySurface.Call(uintptr(disp), uintptr(surf)) - return r != 0 -} - -func eglDestroyContext(disp _EGLDisplay, ctx _EGLContext) bool { - r, _, _ := _eglDestroyContext.Call(uintptr(disp), uintptr(ctx)) - return r != 0 -} - -func eglGetConfigAttrib(disp _EGLDisplay, cfg _EGLConfig, attr _EGLint) (_EGLint, bool) { - var val uintptr - r, _, _ := _eglGetConfigAttrib.Call(uintptr(disp), uintptr(cfg), uintptr(attr), uintptr(unsafe.Pointer(&val))) - return _EGLint(val), r != 0 -} - -func eglGetDisplay(disp NativeDisplayType) _EGLDisplay { - d, _, _ := _eglGetDisplay.Call(uintptr(disp)) - return _EGLDisplay(d) -} - -func eglGetError() _EGLint { - e, _, _ := _eglGetError.Call() - return _EGLint(e) -} - -func eglInitialize(disp _EGLDisplay) (_EGLint, _EGLint, bool) { - var maj, min uintptr - r, _, _ := _eglInitialize.Call(uintptr(disp), uintptr(unsafe.Pointer(&maj)), uintptr(unsafe.Pointer(&min))) - return _EGLint(maj), _EGLint(min), r != 0 -} - -func eglMakeCurrent(disp _EGLDisplay, draw, read _EGLSurface, ctx _EGLContext) bool { - r, _, _ := _eglMakeCurrent.Call(uintptr(disp), uintptr(draw), uintptr(read), uintptr(ctx)) - return r != 0 -} - -func eglReleaseThread() bool { - r, _, _ := _eglReleaseThread.Call() - return r != 0 -} - -func eglSwapInterval(disp _EGLDisplay, interval _EGLint) bool { - r, _, _ := _eglSwapInterval.Call(uintptr(disp), uintptr(interval)) - return r != 0 -} - -func eglSwapBuffers(disp _EGLDisplay, surf _EGLSurface) bool { - r, _, _ := _eglSwapBuffers.Call(uintptr(disp), uintptr(surf)) - return r != 0 -} - -func eglTerminate(disp _EGLDisplay) bool { - r, _, _ := _eglTerminate.Call(uintptr(disp)) - return r != 0 -} - -func eglQueryString(disp _EGLDisplay, name _EGLint) string { - r, _, _ := _eglQueryString.Call(uintptr(disp), uintptr(name)) - return gunsafe.GoString(gunsafe.SliceOf(r)) -} - -// issue34474KeepAlive calls runtime.KeepAlive as a -// workaround for golang.org/issue/34474. -func issue34474KeepAlive(v interface{}) { - runtime.KeepAlive(v) -} diff --git a/vendor/gioui.org/app/internal/glimpl/gl.go b/vendor/gioui.org/app/internal/glimpl/gl.go deleted file mode 100644 index 5b23e4e..0000000 --- a/vendor/gioui.org/app/internal/glimpl/gl.go +++ /dev/null @@ -1,541 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -// +build darwin linux freebsd openbsd - -package glimpl - -import ( - "runtime" - "strings" - "unsafe" - - "gioui.org/gpu/gl" -) - -/* -#cgo CFLAGS: -Werror -#cgo linux,!android pkg-config: glesv2 -#cgo linux freebsd LDFLAGS: -ldl -#cgo freebsd openbsd android LDFLAGS: -lGLESv2 -#cgo freebsd CFLAGS: -I/usr/local/include -#cgo freebsd LDFLAGS: -L/usr/local/lib -#cgo openbsd CFLAGS: -I/usr/X11R6/include -#cgo openbsd LDFLAGS: -L/usr/X11R6/lib -#cgo darwin,!ios CFLAGS: -DGL_SILENCE_DEPRECATION -#cgo darwin,!ios LDFLAGS: -framework OpenGL -#cgo darwin,ios CFLAGS: -DGLES_SILENCE_DEPRECATION -#cgo darwin,ios LDFLAGS: -framework OpenGLES - -#include <stdlib.h> - -#ifdef __APPLE__ - #include "TargetConditionals.h" - #if TARGET_OS_IPHONE - #include <OpenGLES/ES3/gl.h> - #else - #include <OpenGL/gl3.h> - #endif -#else -#define __USE_GNU -#include <dlfcn.h> -#include <GLES2/gl2.h> -#include <GLES3/gl3.h> -#endif - -static void (*_glBindBufferBase)(GLenum target, GLuint index, GLuint buffer); -static GLuint (*_glGetUniformBlockIndex)(GLuint program, const GLchar *uniformBlockName); -static void (*_glUniformBlockBinding)(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); -static void (*_glInvalidateFramebuffer)(GLenum target, GLsizei numAttachments, const GLenum *attachments); - -static void (*_glBeginQuery)(GLenum target, GLuint id); -static void (*_glDeleteQueries)(GLsizei n, const GLuint *ids); -static void (*_glEndQuery)(GLenum target); -static void (*_glGenQueries)(GLsizei n, GLuint *ids); -static void (*_glGetQueryObjectuiv)(GLuint id, GLenum pname, GLuint *params); -static const GLubyte* (*_glGetStringi)(GLenum name, GLuint index); - -// The pointer-free version of glVertexAttribPointer, to avoid the Cgo pointer checks. -__attribute__ ((visibility ("hidden"))) void gio_glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, uintptr_t offset) { - glVertexAttribPointer(index, size, type, normalized, stride, (const GLvoid *)offset); -} - -// The pointer-free version of glDrawElements, to avoid the Cgo pointer checks. -__attribute__ ((visibility ("hidden"))) void gio_glDrawElements(GLenum mode, GLsizei count, GLenum type, const uintptr_t offset) { - glDrawElements(mode, count, type, (const GLvoid *)offset); -} - -__attribute__ ((visibility ("hidden"))) void gio_glBindBufferBase(GLenum target, GLuint index, GLuint buffer) { - _glBindBufferBase(target, index, buffer); -} - -__attribute__ ((visibility ("hidden"))) void gio_glUniformBlockBinding(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding) { - _glUniformBlockBinding(program, uniformBlockIndex, uniformBlockBinding); -} - -__attribute__ ((visibility ("hidden"))) GLuint gio_glGetUniformBlockIndex(GLuint program, const GLchar *uniformBlockName) { - return _glGetUniformBlockIndex(program, uniformBlockName); -} - -__attribute__ ((visibility ("hidden"))) void gio_glInvalidateFramebuffer(GLenum target, GLenum attachment) { - // gl.Framebuffer invalidation is just a hint and can safely be ignored. - if (_glInvalidateFramebuffer != NULL) { - _glInvalidateFramebuffer(target, 1, &attachment); - } -} - -__attribute__ ((visibility ("hidden"))) void gio_glBeginQuery(GLenum target, GLenum attachment) { - _glBeginQuery(target, attachment); -} - -__attribute__ ((visibility ("hidden"))) void gio_glDeleteQueries(GLsizei n, const GLuint *ids) { - _glDeleteQueries(n, ids); -} - -__attribute__ ((visibility ("hidden"))) void gio_glEndQuery(GLenum target) { - _glEndQuery(target); -} - -__attribute__ ((visibility ("hidden"))) const GLubyte* gio_glGetStringi(GLenum name, GLuint index) { - if (_glGetStringi == NULL) { - return NULL; - } - return _glGetStringi(name, index); -} - -__attribute__ ((visibility ("hidden"))) void gio_glGenQueries(GLsizei n, GLuint *ids) { - _glGenQueries(n, ids); -} - -__attribute__ ((visibility ("hidden"))) void gio_glGetQueryObjectuiv(GLuint id, GLenum pname, GLuint *params) { - _glGetQueryObjectuiv(id, pname, params); -} - -__attribute__((constructor)) static void gio_loadGLFunctions() { -#ifdef __APPLE__ - #if TARGET_OS_IPHONE - _glInvalidateFramebuffer = glInvalidateFramebuffer; - _glBeginQuery = glBeginQuery; - _glDeleteQueries = glDeleteQueries; - _glEndQuery = glEndQuery; - _glGenQueries = glGenQueries; - _glGetQueryObjectuiv = glGetQueryObjectuiv; - #endif - _glBindBufferBase = glBindBufferBase; - _glGetUniformBlockIndex = glGetUniformBlockIndex; - _glUniformBlockBinding = glUniformBlockBinding; - _glGetStringi = glGetStringi; -#else - // Load libGLESv3 if available. - dlopen("libGLESv3.so", RTLD_NOW | RTLD_GLOBAL); - _glBindBufferBase = dlsym(RTLD_DEFAULT, "glBindBufferBase"); - _glGetUniformBlockIndex = dlsym(RTLD_DEFAULT, "glGetUniformBlockIndex"); - _glUniformBlockBinding = dlsym(RTLD_DEFAULT, "glUniformBlockBinding"); - _glInvalidateFramebuffer = dlsym(RTLD_DEFAULT, "glInvalidateFramebuffer"); - _glGetStringi = dlsym(RTLD_DEFAULT, "glGetStringi"); - // Fall back to EXT_invalidate_framebuffer if available. - if (_glInvalidateFramebuffer == NULL) { - _glInvalidateFramebuffer = dlsym(RTLD_DEFAULT, "glDiscardFramebufferEXT"); - } - - _glBeginQuery = dlsym(RTLD_DEFAULT, "glBeginQuery"); - if (_glBeginQuery == NULL) - _glBeginQuery = dlsym(RTLD_DEFAULT, "glBeginQueryEXT"); - _glDeleteQueries = dlsym(RTLD_DEFAULT, "glDeleteQueries"); - if (_glDeleteQueries == NULL) - _glDeleteQueries = dlsym(RTLD_DEFAULT, "glDeleteQueriesEXT"); - _glEndQuery = dlsym(RTLD_DEFAULT, "glEndQuery"); - if (_glEndQuery == NULL) - _glEndQuery = dlsym(RTLD_DEFAULT, "glEndQueryEXT"); - _glGenQueries = dlsym(RTLD_DEFAULT, "glGenQueries"); - if (_glGenQueries == NULL) - _glGenQueries = dlsym(RTLD_DEFAULT, "glGenQueriesEXT"); - _glGetQueryObjectuiv = dlsym(RTLD_DEFAULT, "glGetQueryObjectuiv"); - if (_glGetQueryObjectuiv == NULL) - _glGetQueryObjectuiv = dlsym(RTLD_DEFAULT, "glGetQueryObjectuivEXT"); -#endif -} -*/ -import "C" - -type Functions struct { - // gl.Query caches. - uints [100]C.GLuint - ints [100]C.GLint -} - -func (f *Functions) ActiveTexture(texture gl.Enum) { - C.glActiveTexture(C.GLenum(texture)) -} - -func (f *Functions) AttachShader(p gl.Program, s gl.Shader) { - C.glAttachShader(C.GLuint(p.V), C.GLuint(s.V)) -} - -func (f *Functions) BeginQuery(target gl.Enum, query gl.Query) { - C.gio_glBeginQuery(C.GLenum(target), C.GLenum(query.V)) -} - -func (f *Functions) BindAttribLocation(p gl.Program, a gl.Attrib, name string) { - cname := C.CString(name) - defer C.free(unsafe.Pointer(cname)) - C.glBindAttribLocation(C.GLuint(p.V), C.GLuint(a), cname) -} - -func (f *Functions) BindBufferBase(target gl.Enum, index int, b gl.Buffer) { - C.gio_glBindBufferBase(C.GLenum(target), C.GLuint(index), C.GLuint(b.V)) -} - -func (f *Functions) BindBuffer(target gl.Enum, b gl.Buffer) { - C.glBindBuffer(C.GLenum(target), C.GLuint(b.V)) -} - -func (f *Functions) BindFramebuffer(target gl.Enum, fb gl.Framebuffer) { - C.glBindFramebuffer(C.GLenum(target), C.GLuint(fb.V)) -} - -func (f *Functions) BindRenderbuffer(target gl.Enum, fb gl.Renderbuffer) { - C.glBindRenderbuffer(C.GLenum(target), C.GLuint(fb.V)) -} - -func (f *Functions) BindTexture(target gl.Enum, t gl.Texture) { - C.glBindTexture(C.GLenum(target), C.GLuint(t.V)) -} - -func (f *Functions) BlendEquation(mode gl.Enum) { - C.glBlendEquation(C.GLenum(mode)) -} - -func (f *Functions) BlendFunc(sfactor, dfactor gl.Enum) { - C.glBlendFunc(C.GLenum(sfactor), C.GLenum(dfactor)) -} - -func (f *Functions) BufferData(target gl.Enum, src []byte, usage gl.Enum) { - var p unsafe.Pointer - if len(src) > 0 { - p = unsafe.Pointer(&src[0]) - } - C.glBufferData(C.GLenum(target), C.GLsizeiptr(len(src)), p, C.GLenum(usage)) -} - -func (f *Functions) CheckFramebufferStatus(target gl.Enum) gl.Enum { - return gl.Enum(C.glCheckFramebufferStatus(C.GLenum(target))) -} - -func (f *Functions) Clear(mask gl.Enum) { - C.glClear(C.GLbitfield(mask)) -} - -func (f *Functions) ClearColor(red float32, green float32, blue float32, alpha float32) { - C.glClearColor(C.GLfloat(red), C.GLfloat(green), C.GLfloat(blue), C.GLfloat(alpha)) -} - -func (f *Functions) ClearDepthf(d float32) { - C.glClearDepthf(C.GLfloat(d)) -} - -func (f *Functions) CompileShader(s gl.Shader) { - C.glCompileShader(C.GLuint(s.V)) -} - -func (f *Functions) CreateBuffer() gl.Buffer { - C.glGenBuffers(1, &f.uints[0]) - return gl.Buffer{uint(f.uints[0])} -} - -func (f *Functions) CreateFramebuffer() gl.Framebuffer { - C.glGenFramebuffers(1, &f.uints[0]) - return gl.Framebuffer{uint(f.uints[0])} -} - -func (f *Functions) CreateProgram() gl.Program { - return gl.Program{uint(C.glCreateProgram())} -} - -func (f *Functions) CreateQuery() gl.Query { - C.gio_glGenQueries(1, &f.uints[0]) - return gl.Query{uint(f.uints[0])} -} - -func (f *Functions) CreateRenderbuffer() gl.Renderbuffer { - C.glGenRenderbuffers(1, &f.uints[0]) - return gl.Renderbuffer{uint(f.uints[0])} -} - -func (f *Functions) CreateShader(ty gl.Enum) gl.Shader { - return gl.Shader{uint(C.glCreateShader(C.GLenum(ty)))} -} - -func (f *Functions) CreateTexture() gl.Texture { - C.glGenTextures(1, &f.uints[0]) - return gl.Texture{uint(f.uints[0])} -} - -func (f *Functions) DeleteBuffer(v gl.Buffer) { - f.uints[0] = C.GLuint(v.V) - C.glDeleteBuffers(1, &f.uints[0]) -} - -func (f *Functions) DeleteFramebuffer(v gl.Framebuffer) { - f.uints[0] = C.GLuint(v.V) - C.glDeleteFramebuffers(1, &f.uints[0]) -} - -func (f *Functions) DeleteProgram(p gl.Program) { - C.glDeleteProgram(C.GLuint(p.V)) -} - -func (f *Functions) DeleteQuery(query gl.Query) { - f.uints[0] = C.GLuint(query.V) - C.gio_glDeleteQueries(1, &f.uints[0]) -} - -func (f *Functions) DeleteRenderbuffer(v gl.Renderbuffer) { - f.uints[0] = C.GLuint(v.V) - C.glDeleteRenderbuffers(1, &f.uints[0]) -} - -func (f *Functions) DeleteShader(s gl.Shader) { - C.glDeleteShader(C.GLuint(s.V)) -} - -func (f *Functions) DeleteTexture(v gl.Texture) { - f.uints[0] = C.GLuint(v.V) - C.glDeleteTextures(1, &f.uints[0]) -} - -func (f *Functions) DepthFunc(v gl.Enum) { - C.glDepthFunc(C.GLenum(v)) -} - -func (f *Functions) DepthMask(mask bool) { - m := C.GLboolean(C.GL_FALSE) - if mask { - m = C.GLboolean(C.GL_TRUE) - } - C.glDepthMask(m) -} - -func (f *Functions) DisableVertexAttribArray(a gl.Attrib) { - C.glDisableVertexAttribArray(C.GLuint(a)) -} - -func (f *Functions) Disable(cap gl.Enum) { - C.glDisable(C.GLenum(cap)) -} - -func (f *Functions) DrawArrays(mode gl.Enum, first int, count int) { - C.glDrawArrays(C.GLenum(mode), C.GLint(first), C.GLsizei(count)) -} - -func (f *Functions) DrawElements(mode gl.Enum, count int, ty gl.Enum, offset int) { - C.gio_glDrawElements(C.GLenum(mode), C.GLsizei(count), C.GLenum(ty), C.uintptr_t(offset)) -} - -func (f *Functions) Enable(cap gl.Enum) { - C.glEnable(C.GLenum(cap)) -} - -func (f *Functions) EndQuery(target gl.Enum) { - C.gio_glEndQuery(C.GLenum(target)) -} - -func (f *Functions) EnableVertexAttribArray(a gl.Attrib) { - C.glEnableVertexAttribArray(C.GLuint(a)) -} - -func (f *Functions) Finish() { - C.glFinish() -} - -func (f *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarget gl.Enum, renderbuffer gl.Renderbuffer) { - C.glFramebufferRenderbuffer(C.GLenum(target), C.GLenum(attachment), C.GLenum(renderbuffertarget), C.GLuint(renderbuffer.V)) -} - -func (f *Functions) FramebufferTexture2D(target, attachment, texTarget gl.Enum, t gl.Texture, level int) { - C.glFramebufferTexture2D(C.GLenum(target), C.GLenum(attachment), C.GLenum(texTarget), C.GLuint(t.V), C.GLint(level)) -} - -func (c *Functions) GetBinding(pname gl.Enum) gl.Object { - return gl.Object{uint(c.GetInteger(pname))} -} - -func (f *Functions) GetError() gl.Enum { - return gl.Enum(C.glGetError()) -} - -func (f *Functions) GetRenderbufferParameteri(target, pname gl.Enum) int { - C.glGetRenderbufferParameteriv(C.GLenum(target), C.GLenum(pname), &f.ints[0]) - return int(f.ints[0]) -} - -func (f *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname gl.Enum) int { - C.glGetFramebufferAttachmentParameteriv(C.GLenum(target), C.GLenum(attachment), C.GLenum(pname), &f.ints[0]) - return int(f.ints[0]) -} - -func (f *Functions) GetInteger(pname gl.Enum) int { - C.glGetIntegerv(C.GLenum(pname), &f.ints[0]) - return int(f.ints[0]) -} - -func (f *Functions) GetProgrami(p gl.Program, pname gl.Enum) int { - C.glGetProgramiv(C.GLuint(p.V), C.GLenum(pname), &f.ints[0]) - return int(f.ints[0]) -} - -func (f *Functions) GetProgramInfoLog(p gl.Program) string { - n := f.GetProgrami(p, gl.INFO_LOG_LENGTH) - buf := make([]byte, n) - C.glGetProgramInfoLog(C.GLuint(p.V), C.GLsizei(len(buf)), nil, (*C.GLchar)(unsafe.Pointer(&buf[0]))) - return string(buf) -} - -func (f *Functions) GetQueryObjectuiv(query gl.Query, pname gl.Enum) uint { - C.gio_glGetQueryObjectuiv(C.GLuint(query.V), C.GLenum(pname), &f.uints[0]) - return uint(f.uints[0]) -} - -func (f *Functions) GetShaderi(s gl.Shader, pname gl.Enum) int { - C.glGetShaderiv(C.GLuint(s.V), C.GLenum(pname), &f.ints[0]) - return int(f.ints[0]) -} - -func (f *Functions) GetShaderInfoLog(s gl.Shader) string { - n := f.GetShaderi(s, gl.INFO_LOG_LENGTH) - buf := make([]byte, n) - C.glGetShaderInfoLog(C.GLuint(s.V), C.GLsizei(len(buf)), nil, (*C.GLchar)(unsafe.Pointer(&buf[0]))) - return string(buf) -} - -func (f *Functions) GetStringi(pname gl.Enum, index int) string { - str := C.gio_glGetStringi(C.GLenum(pname), C.GLuint(index)) - if str == nil { - return "" - } - return C.GoString((*C.char)(unsafe.Pointer(str))) -} - -func (f *Functions) GetString(pname gl.Enum) string { - switch { - case runtime.GOOS == "darwin" && pname == gl.EXTENSIONS: - // macOS OpenGL 3 core profile doesn't support glGetString(GL_EXTENSIONS). - // Use glGetStringi(GL_EXTENSIONS, <index>). - var exts []string - nexts := f.GetInteger(gl.NUM_EXTENSIONS) - for i := 0; i < nexts; i++ { - ext := f.GetStringi(gl.EXTENSIONS, i) - exts = append(exts, ext) - } - return strings.Join(exts, " ") - default: - str := C.glGetString(C.GLenum(pname)) - return C.GoString((*C.char)(unsafe.Pointer(str))) - } -} - -func (f *Functions) GetUniformBlockIndex(p gl.Program, name string) uint { - cname := C.CString(name) - defer C.free(unsafe.Pointer(cname)) - return uint(C.gio_glGetUniformBlockIndex(C.GLuint(p.V), cname)) -} - -func (f *Functions) GetUniformLocation(p gl.Program, name string) gl.Uniform { - cname := C.CString(name) - defer C.free(unsafe.Pointer(cname)) - return gl.Uniform{int(C.glGetUniformLocation(C.GLuint(p.V), cname))} -} - -func (f *Functions) InvalidateFramebuffer(target, attachment gl.Enum) { - C.gio_glInvalidateFramebuffer(C.GLenum(target), C.GLenum(attachment)) -} - -func (f *Functions) LinkProgram(p gl.Program) { - C.glLinkProgram(C.GLuint(p.V)) -} - -func (f *Functions) PixelStorei(pname gl.Enum, param int32) { - C.glPixelStorei(C.GLenum(pname), C.GLint(param)) -} - -func (f *Functions) Scissor(x, y, width, height int32) { - C.glScissor(C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height)) -} - -func (f *Functions) ReadPixels(x, y, width, height int, format, ty gl.Enum, data []byte) { - var p unsafe.Pointer - if len(data) > 0 { - p = unsafe.Pointer(&data[0]) - } - C.glReadPixels(C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height), C.GLenum(format), C.GLenum(ty), p) -} - -func (f *Functions) RenderbufferStorage(target, internalformat gl.Enum, width, height int) { - C.glRenderbufferStorage(C.GLenum(target), C.GLenum(internalformat), C.GLsizei(width), C.GLsizei(height)) -} - -func (f *Functions) ShaderSource(s gl.Shader, src string) { - csrc := C.CString(src) - defer C.free(unsafe.Pointer(csrc)) - strlen := C.GLint(len(src)) - C.glShaderSource(C.GLuint(s.V), 1, &csrc, &strlen) -} - -func (f *Functions) TexImage2D(target gl.Enum, level int, internalFormat int, width int, height int, format gl.Enum, ty gl.Enum, data []byte) { - var p unsafe.Pointer - if len(data) > 0 { - p = unsafe.Pointer(&data[0]) - } - C.glTexImage2D(C.GLenum(target), C.GLint(level), C.GLint(internalFormat), C.GLsizei(width), C.GLsizei(height), 0, C.GLenum(format), C.GLenum(ty), p) -} - -func (f *Functions) TexSubImage2D(target gl.Enum, level int, x int, y int, width int, height int, format gl.Enum, ty gl.Enum, data []byte) { - var p unsafe.Pointer - if len(data) > 0 { - p = unsafe.Pointer(&data[0]) - } - C.glTexSubImage2D(C.GLenum(target), C.GLint(level), C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height), C.GLenum(format), C.GLenum(ty), p) -} - -func (f *Functions) TexParameteri(target, pname gl.Enum, param int) { - C.glTexParameteri(C.GLenum(target), C.GLenum(pname), C.GLint(param)) -} - -func (f *Functions) UniformBlockBinding(p gl.Program, uniformBlockIndex uint, uniformBlockBinding uint) { - C.gio_glUniformBlockBinding(C.GLuint(p.V), C.GLuint(uniformBlockIndex), C.GLuint(uniformBlockBinding)) -} - -func (f *Functions) Uniform1f(dst gl.Uniform, v float32) { - C.glUniform1f(C.GLint(dst.V), C.GLfloat(v)) -} - -func (f *Functions) Uniform1i(dst gl.Uniform, v int) { - C.glUniform1i(C.GLint(dst.V), C.GLint(v)) -} - -func (f *Functions) Uniform2f(dst gl.Uniform, v0 float32, v1 float32) { - C.glUniform2f(C.GLint(dst.V), C.GLfloat(v0), C.GLfloat(v1)) -} - -func (f *Functions) Uniform3f(dst gl.Uniform, v0 float32, v1 float32, v2 float32) { - C.glUniform3f(C.GLint(dst.V), C.GLfloat(v0), C.GLfloat(v1), C.GLfloat(v2)) -} - -func (f *Functions) Uniform4f(dst gl.Uniform, v0 float32, v1 float32, v2 float32, v3 float32) { - C.glUniform4f(C.GLint(dst.V), C.GLfloat(v0), C.GLfloat(v1), C.GLfloat(v2), C.GLfloat(v3)) -} - -func (f *Functions) UseProgram(p gl.Program) { - C.glUseProgram(C.GLuint(p.V)) -} - -func (f *Functions) VertexAttribPointer(dst gl.Attrib, size int, ty gl.Enum, normalized bool, stride int, offset int) { - var n C.GLboolean = C.GL_FALSE - if normalized { - n = C.GL_TRUE - } - C.gio_glVertexAttribPointer(C.GLuint(dst), C.GLint(size), C.GLenum(ty), n, C.GLsizei(stride), C.uintptr_t(offset)) -} - -func (f *Functions) Viewport(x int, y int, width int, height int) { - C.glViewport(C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height)) -} diff --git a/vendor/gioui.org/app/internal/glimpl/gl_js.go b/vendor/gioui.org/app/internal/glimpl/gl_js.go deleted file mode 100644 index e4291d0..0000000 --- a/vendor/gioui.org/app/internal/glimpl/gl_js.go +++ /dev/null @@ -1,339 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package glimpl - -import ( - "errors" - "strings" - "syscall/js" - - "gioui.org/gpu/gl" -) - -type Functions struct { - Ctx js.Value - EXT_disjoint_timer_query js.Value - EXT_disjoint_timer_query_webgl2 js.Value - - // Cached JS arrays. - byteBuf js.Value - int32Buf js.Value -} - -func (f *Functions) Init(version int) error { - if version < 2 { - f.EXT_disjoint_timer_query = f.getExtension("EXT_disjoint_timer_query") - if f.getExtension("OES_texture_half_float").IsNull() && f.getExtension("OES_texture_float").IsNull() { - return errors.New("gl: no support for neither OES_texture_half_float nor OES_texture_float") - } - if f.getExtension("EXT_sRGB").IsNull() { - return errors.New("gl: EXT_sRGB not supported") - } - } else { - // WebGL2 extensions. - f.EXT_disjoint_timer_query_webgl2 = f.getExtension("EXT_disjoint_timer_query_webgl2") - if f.getExtension("EXT_color_buffer_half_float").IsNull() && f.getExtension("EXT_color_buffer_float").IsNull() { - return errors.New("gl: no support for neither EXT_color_buffer_half_float nor EXT_color_buffer_float") - } - } - return nil -} - -func (f *Functions) getExtension(name string) js.Value { - return f.Ctx.Call("getExtension", name) -} - -func (f *Functions) ActiveTexture(t gl.Enum) { - f.Ctx.Call("activeTexture", int(t)) -} -func (f *Functions) AttachShader(p gl.Program, s gl.Shader) { - f.Ctx.Call("attachShader", js.Value(p), js.Value(s)) -} -func (f *Functions) BeginQuery(target gl.Enum, query gl.Query) { - if !f.EXT_disjoint_timer_query_webgl2.IsNull() { - f.Ctx.Call("beginQuery", int(target), js.Value(query)) - } else { - f.EXT_disjoint_timer_query.Call("beginQueryEXT", int(target), js.Value(query)) - } -} -func (f *Functions) BindAttribLocation(p gl.Program, a gl.Attrib, name string) { - f.Ctx.Call("bindAttribLocation", js.Value(p), int(a), name) -} -func (f *Functions) BindBuffer(target gl.Enum, b gl.Buffer) { - f.Ctx.Call("bindBuffer", int(target), js.Value(b)) -} -func (f *Functions) BindBufferBase(target gl.Enum, index int, b gl.Buffer) { - f.Ctx.Call("bindBufferBase", int(target), index, js.Value(b)) -} -func (f *Functions) BindFramebuffer(target gl.Enum, fb gl.Framebuffer) { - f.Ctx.Call("bindFramebuffer", int(target), js.Value(fb)) -} -func (f *Functions) BindRenderbuffer(target gl.Enum, rb gl.Renderbuffer) { - f.Ctx.Call("bindRenderbuffer", int(target), js.Value(rb)) -} -func (f *Functions) BindTexture(target gl.Enum, t gl.Texture) { - f.Ctx.Call("bindTexture", int(target), js.Value(t)) -} -func (f *Functions) BlendEquation(mode gl.Enum) { - f.Ctx.Call("blendEquation", int(mode)) -} -func (f *Functions) BlendFunc(sfactor, dfactor gl.Enum) { - f.Ctx.Call("blendFunc", int(sfactor), int(dfactor)) -} -func (f *Functions) BufferData(target gl.Enum, src []byte, usage gl.Enum) { - f.Ctx.Call("bufferData", int(target), f.byteArrayOf(src), int(usage)) -} -func (f *Functions) CheckFramebufferStatus(target gl.Enum) gl.Enum { - return gl.Enum(f.Ctx.Call("checkFramebufferStatus", int(target)).Int()) -} -func (f *Functions) Clear(mask gl.Enum) { - f.Ctx.Call("clear", int(mask)) -} -func (f *Functions) ClearColor(red, green, blue, alpha float32) { - f.Ctx.Call("clearColor", red, green, blue, alpha) -} -func (f *Functions) ClearDepthf(d float32) { - f.Ctx.Call("clearDepth", d) -} -func (f *Functions) CompileShader(s gl.Shader) { - f.Ctx.Call("compileShader", js.Value(s)) -} -func (f *Functions) CreateBuffer() gl.Buffer { - return gl.Buffer(f.Ctx.Call("createBuffer")) -} -func (f *Functions) CreateFramebuffer() gl.Framebuffer { - return gl.Framebuffer(f.Ctx.Call("createFramebuffer")) -} -func (f *Functions) CreateProgram() gl.Program { - return gl.Program(f.Ctx.Call("createProgram")) -} -func (f *Functions) CreateQuery() gl.Query { - return gl.Query(f.Ctx.Call("createQuery")) -} -func (f *Functions) CreateRenderbuffer() gl.Renderbuffer { - return gl.Renderbuffer(f.Ctx.Call("createRenderbuffer")) -} -func (f *Functions) CreateShader(ty gl.Enum) gl.Shader { - return gl.Shader(f.Ctx.Call("createShader", int(ty))) -} -func (f *Functions) CreateTexture() gl.Texture { - return gl.Texture(f.Ctx.Call("createTexture")) -} -func (f *Functions) DeleteBuffer(v gl.Buffer) { - f.Ctx.Call("deleteBuffer", js.Value(v)) -} -func (f *Functions) DeleteFramebuffer(v gl.Framebuffer) { - f.Ctx.Call("deleteFramebuffer", js.Value(v)) -} -func (f *Functions) DeleteProgram(p gl.Program) { - f.Ctx.Call("deleteProgram", js.Value(p)) -} -func (f *Functions) DeleteQuery(query gl.Query) { - if !f.EXT_disjoint_timer_query_webgl2.IsNull() { - f.Ctx.Call("deleteQuery", js.Value(query)) - } else { - f.EXT_disjoint_timer_query.Call("deleteQueryEXT", js.Value(query)) - } -} -func (f *Functions) DeleteShader(s gl.Shader) { - f.Ctx.Call("deleteShader", js.Value(s)) -} -func (f *Functions) DeleteRenderbuffer(v gl.Renderbuffer) { - f.Ctx.Call("deleteRenderbuffer", js.Value(v)) -} -func (f *Functions) DeleteTexture(v gl.Texture) { - f.Ctx.Call("deleteTexture", js.Value(v)) -} -func (f *Functions) DepthFunc(fn gl.Enum) { - f.Ctx.Call("depthFunc", int(fn)) -} -func (f *Functions) DepthMask(mask bool) { - f.Ctx.Call("depthMask", mask) -} -func (f *Functions) DisableVertexAttribArray(a gl.Attrib) { - f.Ctx.Call("disableVertexAttribArray", int(a)) -} -func (f *Functions) Disable(cap gl.Enum) { - f.Ctx.Call("disable", int(cap)) -} -func (f *Functions) DrawArrays(mode gl.Enum, first, count int) { - f.Ctx.Call("drawArrays", int(mode), first, count) -} -func (f *Functions) DrawElements(mode gl.Enum, count int, ty gl.Enum, offset int) { - f.Ctx.Call("drawElements", int(mode), count, int(ty), offset) -} -func (f *Functions) Enable(cap gl.Enum) { - f.Ctx.Call("enable", int(cap)) -} -func (f *Functions) EnableVertexAttribArray(a gl.Attrib) { - f.Ctx.Call("enableVertexAttribArray", int(a)) -} -func (f *Functions) EndQuery(target gl.Enum) { - if !f.EXT_disjoint_timer_query_webgl2.IsNull() { - f.Ctx.Call("endQuery", int(target)) - } else { - f.EXT_disjoint_timer_query.Call("endQueryEXT", int(target)) - } -} -func (f *Functions) Finish() { - f.Ctx.Call("finish") -} -func (f *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarget gl.Enum, renderbuffer gl.Renderbuffer) { - f.Ctx.Call("framebufferRenderbuffer", int(target), int(attachment), int(renderbuffertarget), js.Value(renderbuffer)) -} -func (f *Functions) FramebufferTexture2D(target, attachment, texTarget gl.Enum, t gl.Texture, level int) { - f.Ctx.Call("framebufferTexture2D", int(target), int(attachment), int(texTarget), js.Value(t), level) -} -func (f *Functions) GetError() gl.Enum { - return gl.Enum(f.Ctx.Call("getError").Int()) -} -func (f *Functions) GetRenderbufferParameteri(target, pname gl.Enum) int { - return paramVal(f.Ctx.Call("getRenderbufferParameteri", int(pname))) -} -func (f *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname gl.Enum) int { - return paramVal(f.Ctx.Call("getFramebufferAttachmentParameter", int(target), int(attachment), int(pname))) -} -func (f *Functions) GetBinding(pname gl.Enum) gl.Object { - return gl.Object(f.Ctx.Call("getParameter", int(pname))) -} -func (f *Functions) GetInteger(pname gl.Enum) int { - return paramVal(f.Ctx.Call("getParameter", int(pname))) -} -func (f *Functions) GetProgrami(p gl.Program, pname gl.Enum) int { - return paramVal(f.Ctx.Call("getProgramParameter", js.Value(p), int(pname))) -} -func (f *Functions) GetProgramInfoLog(p gl.Program) string { - return f.Ctx.Call("getProgramInfoLog", js.Value(p)).String() -} -func (f *Functions) GetQueryObjectuiv(query gl.Query, pname gl.Enum) uint { - if !f.EXT_disjoint_timer_query_webgl2.IsNull() { - return uint(paramVal(f.Ctx.Call("getQueryParameter", js.Value(query), int(pname)))) - } else { - return uint(paramVal(f.EXT_disjoint_timer_query.Call("getQueryObjectEXT", js.Value(query), int(pname)))) - } -} -func (f *Functions) GetShaderi(s gl.Shader, pname gl.Enum) int { - return paramVal(f.Ctx.Call("getShaderParameter", js.Value(s), int(pname))) -} -func (f *Functions) GetShaderInfoLog(s gl.Shader) string { - return f.Ctx.Call("getShaderInfoLog", js.Value(s)).String() -} -func (f *Functions) GetString(pname gl.Enum) string { - switch pname { - case gl.EXTENSIONS: - extsjs := f.Ctx.Call("getSupportedExtensions") - var exts []string - for i := 0; i < extsjs.Length(); i++ { - exts = append(exts, "GL_"+extsjs.Index(i).String()) - } - return strings.Join(exts, " ") - default: - return f.Ctx.Call("getParameter", int(pname)).String() - } -} -func (f *Functions) GetUniformBlockIndex(p gl.Program, name string) uint { - return uint(paramVal(f.Ctx.Call("getUniformBlockIndex", js.Value(p), name))) -} -func (f *Functions) GetUniformLocation(p gl.Program, name string) gl.Uniform { - return gl.Uniform(f.Ctx.Call("getUniformLocation", js.Value(p), name)) -} -func (f *Functions) InvalidateFramebuffer(target, attachment gl.Enum) { - fn := f.Ctx.Get("invalidateFramebuffer") - if !fn.IsUndefined() { - if f.int32Buf.IsUndefined() { - f.int32Buf = js.Global().Get("Int32Array").New(1) - } - f.int32Buf.SetIndex(0, int32(attachment)) - f.Ctx.Call("invalidateFramebuffer", int(target), f.int32Buf) - } -} -func (f *Functions) LinkProgram(p gl.Program) { - f.Ctx.Call("linkProgram", js.Value(p)) -} -func (f *Functions) PixelStorei(pname gl.Enum, param int32) { - f.Ctx.Call("pixelStorei", int(pname), param) -} -func (f *Functions) RenderbufferStorage(target, internalformat gl.Enum, width, height int) { - f.Ctx.Call("renderbufferStorage", int(target), int(internalformat), width, height) -} -func (f *Functions) ReadPixels(x, y, width, height int, format, ty gl.Enum, data []byte) { - f.resizeByteBuffer(len(data)) - f.Ctx.Call("readPixels", x, y, width, height, int(format), int(ty), f.byteBuf) - js.CopyBytesToGo(data, f.byteBuf) -} -func (f *Functions) Scissor(x, y, width, height int32) { - f.Ctx.Call("scissor", x, y, width, height) -} -func (f *Functions) ShaderSource(s gl.Shader, src string) { - f.Ctx.Call("shaderSource", js.Value(s), src) -} -func (f *Functions) TexImage2D(target gl.Enum, level int, internalFormat int, width, height int, format, ty gl.Enum, data []byte) { - f.Ctx.Call("texImage2D", int(target), int(level), int(internalFormat), int(width), int(height), 0, int(format), int(ty), f.byteArrayOf(data)) -} -func (f *Functions) TexSubImage2D(target gl.Enum, level int, x, y, width, height int, format, ty gl.Enum, data []byte) { - f.Ctx.Call("texSubImage2D", int(target), level, x, y, width, height, int(format), int(ty), f.byteArrayOf(data)) -} -func (f *Functions) TexParameteri(target, pname gl.Enum, param int) { - f.Ctx.Call("texParameteri", int(target), int(pname), int(param)) -} -func (f *Functions) UniformBlockBinding(p gl.Program, uniformBlockIndex uint, uniformBlockBinding uint) { - f.Ctx.Call("uniformBlockBinding", js.Value(p), int(uniformBlockIndex), int(uniformBlockBinding)) -} -func (f *Functions) Uniform1f(dst gl.Uniform, v float32) { - f.Ctx.Call("uniform1f", js.Value(dst), v) -} -func (f *Functions) Uniform1i(dst gl.Uniform, v int) { - f.Ctx.Call("uniform1i", js.Value(dst), v) -} -func (f *Functions) Uniform2f(dst gl.Uniform, v0, v1 float32) { - f.Ctx.Call("uniform2f", js.Value(dst), v0, v1) -} -func (f *Functions) Uniform3f(dst gl.Uniform, v0, v1, v2 float32) { - f.Ctx.Call("uniform3f", js.Value(dst), v0, v1, v2) -} -func (f *Functions) Uniform4f(dst gl.Uniform, v0, v1, v2, v3 float32) { - f.Ctx.Call("uniform4f", js.Value(dst), v0, v1, v2, v3) -} -func (f *Functions) UseProgram(p gl.Program) { - f.Ctx.Call("useProgram", js.Value(p)) -} -func (f *Functions) VertexAttribPointer(dst gl.Attrib, size int, ty gl.Enum, normalized bool, stride, offset int) { - f.Ctx.Call("vertexAttribPointer", int(dst), size, int(ty), normalized, stride, offset) -} -func (f *Functions) Viewport(x, y, width, height int) { - f.Ctx.Call("viewport", x, y, width, height) -} - -func (f *Functions) byteArrayOf(data []byte) js.Value { - if len(data) == 0 { - return js.Null() - } - f.resizeByteBuffer(len(data)) - js.CopyBytesToJS(f.byteBuf, data) - return f.byteBuf -} - -func (f *Functions) resizeByteBuffer(n int) { - if n == 0 { - return - } - if !f.byteBuf.IsUndefined() && f.byteBuf.Length() >= n { - return - } - f.byteBuf = js.Global().Get("Uint8Array").New(n) -} - -func paramVal(v js.Value) int { - switch v.Type() { - case js.TypeBoolean: - if b := v.Bool(); b { - return 1 - } else { - return 0 - } - case js.TypeNumber: - return v.Int() - default: - panic("unknown parameter type") - } -} diff --git a/vendor/gioui.org/app/internal/glimpl/gl_windows.go b/vendor/gioui.org/app/internal/glimpl/gl_windows.go deleted file mode 100644 index 4fe1d91..0000000 --- a/vendor/gioui.org/app/internal/glimpl/gl_windows.go +++ /dev/null @@ -1,406 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package glimpl - -import ( - "math" - "runtime" - "syscall" - "unsafe" - - "golang.org/x/sys/windows" - - "gioui.org/gpu/gl" - gunsafe "gioui.org/internal/unsafe" -) - -var ( - LibGLESv2 = windows.NewLazyDLL("libGLESv2.dll") - _glActiveTexture = LibGLESv2.NewProc("glActiveTexture") - _glAttachShader = LibGLESv2.NewProc("glAttachShader") - _glBeginQuery = LibGLESv2.NewProc("glBeginQuery") - _glBindAttribLocation = LibGLESv2.NewProc("glBindAttribLocation") - _glBindBuffer = LibGLESv2.NewProc("glBindBuffer") - _glBindBufferBase = LibGLESv2.NewProc("glBindBufferBase") - _glBindFramebuffer = LibGLESv2.NewProc("glBindFramebuffer") - _glBindRenderbuffer = LibGLESv2.NewProc("glBindRenderbuffer") - _glBindTexture = LibGLESv2.NewProc("glBindTexture") - _glBlendEquation = LibGLESv2.NewProc("glBlendEquation") - _glBlendFunc = LibGLESv2.NewProc("glBlendFunc") - _glBufferData = LibGLESv2.NewProc("glBufferData") - _glCheckFramebufferStatus = LibGLESv2.NewProc("glCheckFramebufferStatus") - _glClear = LibGLESv2.NewProc("glClear") - _glClearColor = LibGLESv2.NewProc("glClearColor") - _glClearDepthf = LibGLESv2.NewProc("glClearDepthf") - _glDeleteQueries = LibGLESv2.NewProc("glDeleteQueries") - _glCompileShader = LibGLESv2.NewProc("glCompileShader") - _glGenBuffers = LibGLESv2.NewProc("glGenBuffers") - _glGenFramebuffers = LibGLESv2.NewProc("glGenFramebuffers") - _glGetUniformBlockIndex = LibGLESv2.NewProc("glGetUniformBlockIndex") - _glCreateProgram = LibGLESv2.NewProc("glCreateProgram") - _glGenRenderbuffers = LibGLESv2.NewProc("glGenRenderbuffers") - _glCreateShader = LibGLESv2.NewProc("glCreateShader") - _glGenTextures = LibGLESv2.NewProc("glGenTextures") - _glDeleteBuffers = LibGLESv2.NewProc("glDeleteBuffers") - _glDeleteFramebuffers = LibGLESv2.NewProc("glDeleteFramebuffers") - _glDeleteProgram = LibGLESv2.NewProc("glDeleteProgram") - _glDeleteShader = LibGLESv2.NewProc("glDeleteShader") - _glDeleteRenderbuffers = LibGLESv2.NewProc("glDeleteRenderbuffers") - _glDeleteTextures = LibGLESv2.NewProc("glDeleteTextures") - _glDepthFunc = LibGLESv2.NewProc("glDepthFunc") - _glDepthMask = LibGLESv2.NewProc("glDepthMask") - _glDisableVertexAttribArray = LibGLESv2.NewProc("glDisableVertexAttribArray") - _glDisable = LibGLESv2.NewProc("glDisable") - _glDrawArrays = LibGLESv2.NewProc("glDrawArrays") - _glDrawElements = LibGLESv2.NewProc("glDrawElements") - _glEnable = LibGLESv2.NewProc("glEnable") - _glEnableVertexAttribArray = LibGLESv2.NewProc("glEnableVertexAttribArray") - _glEndQuery = LibGLESv2.NewProc("glEndQuery") - _glFinish = LibGLESv2.NewProc("glFinish") - _glFramebufferRenderbuffer = LibGLESv2.NewProc("glFramebufferRenderbuffer") - _glFramebufferTexture2D = LibGLESv2.NewProc("glFramebufferTexture2D") - _glGenQueries = LibGLESv2.NewProc("glGenQueries") - _glGetError = LibGLESv2.NewProc("glGetError") - _glGetRenderbufferParameteri = LibGLESv2.NewProc("glGetRenderbufferParameteri") - _glGetFramebufferAttachmentParameteri = LibGLESv2.NewProc("glGetFramebufferAttachmentParameteri") - _glGetIntegerv = LibGLESv2.NewProc("glGetIntegerv") - _glGetProgramiv = LibGLESv2.NewProc("glGetProgramiv") - _glGetProgramInfoLog = LibGLESv2.NewProc("glGetProgramInfoLog") - _glGetQueryObjectuiv = LibGLESv2.NewProc("glGetQueryObjectuiv") - _glGetShaderiv = LibGLESv2.NewProc("glGetShaderiv") - _glGetShaderInfoLog = LibGLESv2.NewProc("glGetShaderInfoLog") - _glGetString = LibGLESv2.NewProc("glGetString") - _glGetUniformLocation = LibGLESv2.NewProc("glGetUniformLocation") - _glInvalidateFramebuffer = LibGLESv2.NewProc("glInvalidateFramebuffer") - _glLinkProgram = LibGLESv2.NewProc("glLinkProgram") - _glPixelStorei = LibGLESv2.NewProc("glPixelStorei") - _glReadPixels = LibGLESv2.NewProc("glReadPixels") - _glRenderbufferStorage = LibGLESv2.NewProc("glRenderbufferStorage") - _glScissor = LibGLESv2.NewProc("glScissor") - _glShaderSource = LibGLESv2.NewProc("glShaderSource") - _glTexImage2D = LibGLESv2.NewProc("glTexImage2D") - _glTexSubImage2D = LibGLESv2.NewProc("glTexSubImage2D") - _glTexParameteri = LibGLESv2.NewProc("glTexParameteri") - _glUniformBlockBinding = LibGLESv2.NewProc("glUniformBlockBinding") - _glUniform1f = LibGLESv2.NewProc("glUniform1f") - _glUniform1i = LibGLESv2.NewProc("glUniform1i") - _glUniform2f = LibGLESv2.NewProc("glUniform2f") - _glUniform3f = LibGLESv2.NewProc("glUniform3f") - _glUniform4f = LibGLESv2.NewProc("glUniform4f") - _glUseProgram = LibGLESv2.NewProc("glUseProgram") - _glVertexAttribPointer = LibGLESv2.NewProc("glVertexAttribPointer") - _glViewport = LibGLESv2.NewProc("glViewport") -) - -type Functions struct { - // gl.Query caches. - int32s [100]int32 -} - -func (c *Functions) ActiveTexture(t gl.Enum) { - syscall.Syscall(_glActiveTexture.Addr(), 1, uintptr(t), 0, 0) -} -func (c *Functions) AttachShader(p gl.Program, s gl.Shader) { - syscall.Syscall(_glAttachShader.Addr(), 2, uintptr(p.V), uintptr(s.V), 0) -} -func (f *Functions) BeginQuery(target gl.Enum, query gl.Query) { - syscall.Syscall(_glBeginQuery.Addr(), 2, uintptr(target), uintptr(query.V), 0) -} -func (c *Functions) BindAttribLocation(p gl.Program, a gl.Attrib, name string) { - cname := cString(name) - c0 := &cname[0] - syscall.Syscall(_glBindAttribLocation.Addr(), 3, uintptr(p.V), uintptr(a), uintptr(unsafe.Pointer(c0))) - issue34474KeepAlive(c) -} -func (c *Functions) BindBuffer(target gl.Enum, b gl.Buffer) { - syscall.Syscall(_glBindBuffer.Addr(), 2, uintptr(target), uintptr(b.V), 0) -} -func (c *Functions) BindBufferBase(target gl.Enum, index int, b gl.Buffer) { - syscall.Syscall(_glBindBufferBase.Addr(), 3, uintptr(target), uintptr(index), uintptr(b.V)) -} -func (c *Functions) BindFramebuffer(target gl.Enum, fb gl.Framebuffer) { - syscall.Syscall(_glBindFramebuffer.Addr(), 2, uintptr(target), uintptr(fb.V), 0) -} -func (c *Functions) BindRenderbuffer(target gl.Enum, rb gl.Renderbuffer) { - syscall.Syscall(_glBindRenderbuffer.Addr(), 2, uintptr(target), uintptr(rb.V), 0) -} -func (c *Functions) BindTexture(target gl.Enum, t gl.Texture) { - syscall.Syscall(_glBindTexture.Addr(), 2, uintptr(target), uintptr(t.V), 0) -} -func (c *Functions) BlendEquation(mode gl.Enum) { - syscall.Syscall(_glBlendEquation.Addr(), 1, uintptr(mode), 0, 0) -} -func (c *Functions) BlendFunc(sfactor, dfactor gl.Enum) { - syscall.Syscall(_glBlendFunc.Addr(), 2, uintptr(sfactor), uintptr(dfactor), 0) -} -func (c *Functions) BufferData(target gl.Enum, src []byte, usage gl.Enum) { - if n := len(src); n == 0 { - syscall.Syscall6(_glBufferData.Addr(), 4, uintptr(target), 0, 0, uintptr(usage), 0, 0) - } else { - s0 := &src[0] - syscall.Syscall6(_glBufferData.Addr(), 4, uintptr(target), uintptr(n), uintptr(unsafe.Pointer(s0)), uintptr(usage), 0, 0) - issue34474KeepAlive(s0) - } -} -func (c *Functions) CheckFramebufferStatus(target gl.Enum) gl.Enum { - s, _, _ := syscall.Syscall(_glCheckFramebufferStatus.Addr(), 1, uintptr(target), 0, 0) - return gl.Enum(s) -} -func (c *Functions) Clear(mask gl.Enum) { - syscall.Syscall(_glClear.Addr(), 1, uintptr(mask), 0, 0) -} -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) -} -func (c *Functions) ClearDepthf(d float32) { - syscall.Syscall(_glClearDepthf.Addr(), 1, uintptr(math.Float32bits(d)), 0, 0) -} -func (c *Functions) CompileShader(s gl.Shader) { - syscall.Syscall(_glCompileShader.Addr(), 1, uintptr(s.V), 0, 0) -} -func (c *Functions) CreateBuffer() gl.Buffer { - var buf uintptr - syscall.Syscall(_glGenBuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&buf)), 0) - return gl.Buffer{uint(buf)} -} -func (c *Functions) CreateFramebuffer() gl.Framebuffer { - var fb uintptr - syscall.Syscall(_glGenFramebuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&fb)), 0) - return gl.Framebuffer{uint(fb)} -} -func (c *Functions) CreateProgram() gl.Program { - p, _, _ := syscall.Syscall(_glCreateProgram.Addr(), 0, 0, 0, 0) - return gl.Program{uint(p)} -} -func (f *Functions) CreateQuery() gl.Query { - var q uintptr - syscall.Syscall(_glGenQueries.Addr(), 2, 1, uintptr(unsafe.Pointer(&q)), 0) - return gl.Query{uint(q)} -} -func (c *Functions) CreateRenderbuffer() gl.Renderbuffer { - var rb uintptr - syscall.Syscall(_glGenRenderbuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&rb)), 0) - return gl.Renderbuffer{uint(rb)} -} -func (c *Functions) CreateShader(ty gl.Enum) gl.Shader { - s, _, _ := syscall.Syscall(_glCreateShader.Addr(), 1, uintptr(ty), 0, 0) - return gl.Shader{uint(s)} -} -func (c *Functions) CreateTexture() gl.Texture { - var t uintptr - syscall.Syscall(_glGenTextures.Addr(), 2, 1, uintptr(unsafe.Pointer(&t)), 0) - return gl.Texture{uint(t)} -} -func (c *Functions) DeleteBuffer(v gl.Buffer) { - syscall.Syscall(_glDeleteBuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v)), 0) -} -func (c *Functions) DeleteFramebuffer(v gl.Framebuffer) { - syscall.Syscall(_glDeleteFramebuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0) -} -func (c *Functions) DeleteProgram(p gl.Program) { - syscall.Syscall(_glDeleteProgram.Addr(), 1, uintptr(p.V), 0, 0) -} -func (f *Functions) DeleteQuery(query gl.Query) { - syscall.Syscall(_glDeleteQueries.Addr(), 2, 1, uintptr(unsafe.Pointer(&query.V)), 0) -} -func (c *Functions) DeleteShader(s gl.Shader) { - syscall.Syscall(_glDeleteShader.Addr(), 1, uintptr(s.V), 0, 0) -} -func (c *Functions) DeleteRenderbuffer(v gl.Renderbuffer) { - syscall.Syscall(_glDeleteRenderbuffers.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0) -} -func (c *Functions) DeleteTexture(v gl.Texture) { - syscall.Syscall(_glDeleteTextures.Addr(), 2, 1, uintptr(unsafe.Pointer(&v.V)), 0) -} -func (c *Functions) DepthFunc(f gl.Enum) { - syscall.Syscall(_glDepthFunc.Addr(), 1, uintptr(f), 0, 0) -} -func (c *Functions) DepthMask(mask bool) { - var m uintptr - if mask { - m = 1 - } - syscall.Syscall(_glDepthMask.Addr(), 1, m, 0, 0) -} -func (c *Functions) DisableVertexAttribArray(a gl.Attrib) { - syscall.Syscall(_glDisableVertexAttribArray.Addr(), 1, uintptr(a), 0, 0) -} -func (c *Functions) Disable(cap gl.Enum) { - syscall.Syscall(_glDisable.Addr(), 1, uintptr(cap), 0, 0) -} -func (c *Functions) DrawArrays(mode gl.Enum, first, count int) { - syscall.Syscall(_glDrawArrays.Addr(), 3, uintptr(mode), uintptr(first), uintptr(count)) -} -func (c *Functions) DrawElements(mode gl.Enum, count int, ty gl.Enum, offset int) { - syscall.Syscall6(_glDrawElements.Addr(), 4, uintptr(mode), uintptr(count), uintptr(ty), uintptr(offset), 0, 0) -} -func (c *Functions) Enable(cap gl.Enum) { - syscall.Syscall(_glEnable.Addr(), 1, uintptr(cap), 0, 0) -} -func (c *Functions) EnableVertexAttribArray(a gl.Attrib) { - syscall.Syscall(_glEnableVertexAttribArray.Addr(), 1, uintptr(a), 0, 0) -} -func (f *Functions) EndQuery(target gl.Enum) { - syscall.Syscall(_glEndQuery.Addr(), 1, uintptr(target), 0, 0) -} -func (c *Functions) Finish() { - syscall.Syscall(_glFinish.Addr(), 0, 0, 0, 0) -} -func (c *Functions) FramebufferRenderbuffer(target, attachment, renderbuffertarget gl.Enum, renderbuffer gl.Renderbuffer) { - syscall.Syscall6(_glFramebufferRenderbuffer.Addr(), 4, uintptr(target), uintptr(attachment), uintptr(renderbuffertarget), uintptr(renderbuffer.V), 0, 0) -} -func (c *Functions) FramebufferTexture2D(target, attachment, texTarget gl.Enum, t gl.Texture, level int) { - syscall.Syscall6(_glFramebufferTexture2D.Addr(), 5, uintptr(target), uintptr(attachment), uintptr(texTarget), uintptr(t.V), uintptr(level), 0) -} -func (f *Functions) GetUniformBlockIndex(p gl.Program, name string) uint { - cname := cString(name) - c0 := &cname[0] - u, _, _ := syscall.Syscall(_glGetUniformBlockIndex.Addr(), 2, uintptr(p.V), uintptr(unsafe.Pointer(c0)), 0) - issue34474KeepAlive(c0) - return uint(u) -} -func (c *Functions) GetBinding(pname gl.Enum) gl.Object { - return gl.Object{uint(c.GetInteger(pname))} -} -func (c *Functions) GetError() gl.Enum { - e, _, _ := syscall.Syscall(_glGetError.Addr(), 0, 0, 0, 0) - return gl.Enum(e) -} -func (c *Functions) GetRenderbufferParameteri(target, pname gl.Enum) int { - p, _, _ := syscall.Syscall(_glGetRenderbufferParameteri.Addr(), 2, uintptr(target), uintptr(pname), 0) - return int(p) -} -func (c *Functions) GetFramebufferAttachmentParameteri(target, attachment, pname gl.Enum) int { - p, _, _ := syscall.Syscall(_glGetFramebufferAttachmentParameteri.Addr(), 3, uintptr(target), uintptr(attachment), uintptr(pname)) - return int(p) -} -func (c *Functions) GetInteger(pname gl.Enum) int { - syscall.Syscall(_glGetIntegerv.Addr(), 2, uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0])), 0) - return int(c.int32s[0]) -} -func (c *Functions) GetProgrami(p gl.Program, pname gl.Enum) int { - syscall.Syscall(_glGetProgramiv.Addr(), 3, uintptr(p.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0]))) - return int(c.int32s[0]) -} -func (c *Functions) GetProgramInfoLog(p gl.Program) string { - n := c.GetProgrami(p, gl.INFO_LOG_LENGTH) - buf := make([]byte, n) - syscall.Syscall6(_glGetProgramInfoLog.Addr(), 4, uintptr(p.V), uintptr(len(buf)), 0, uintptr(unsafe.Pointer(&buf[0])), 0, 0) - return string(buf) -} -func (c *Functions) GetQueryObjectuiv(query gl.Query, pname gl.Enum) uint { - syscall.Syscall(_glGetQueryObjectuiv.Addr(), 3, uintptr(query.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0]))) - return uint(c.int32s[0]) -} -func (c *Functions) GetShaderi(s gl.Shader, pname gl.Enum) int { - syscall.Syscall(_glGetShaderiv.Addr(), 3, uintptr(s.V), uintptr(pname), uintptr(unsafe.Pointer(&c.int32s[0]))) - return int(c.int32s[0]) -} -func (c *Functions) GetShaderInfoLog(s gl.Shader) string { - n := c.GetShaderi(s, gl.INFO_LOG_LENGTH) - buf := make([]byte, n) - syscall.Syscall6(_glGetShaderInfoLog.Addr(), 4, uintptr(s.V), uintptr(len(buf)), 0, uintptr(unsafe.Pointer(&buf[0])), 0, 0) - return string(buf) -} -func (c *Functions) GetString(pname gl.Enum) string { - s, _, _ := syscall.Syscall(_glGetString.Addr(), 1, uintptr(pname), 0, 0) - return gunsafe.GoString(gunsafe.SliceOf(s)) -} -func (c *Functions) GetUniformLocation(p gl.Program, name string) gl.Uniform { - cname := cString(name) - c0 := &cname[0] - u, _, _ := syscall.Syscall(_glGetUniformLocation.Addr(), 2, uintptr(p.V), uintptr(unsafe.Pointer(c0)), 0) - issue34474KeepAlive(c0) - return gl.Uniform{int(u)} -} -func (c *Functions) InvalidateFramebuffer(target, attachment gl.Enum) { - addr := _glInvalidateFramebuffer.Addr() - if addr == 0 { - // InvalidateFramebuffer is just a hint. Skip it if not supported. - return - } - syscall.Syscall(addr, 3, uintptr(target), 1, uintptr(unsafe.Pointer(&attachment))) -} -func (c *Functions) LinkProgram(p gl.Program) { - syscall.Syscall(_glLinkProgram.Addr(), 1, uintptr(p.V), 0, 0) -} -func (c *Functions) PixelStorei(pname gl.Enum, param int32) { - syscall.Syscall(_glPixelStorei.Addr(), 2, uintptr(pname), uintptr(param), 0) -} -func (f *Functions) ReadPixels(x, y, width, height int, format, ty gl.Enum, data []byte) { - 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) - issue34474KeepAlive(d0) -} -func (c *Functions) RenderbufferStorage(target, internalformat gl.Enum, width, height int) { - syscall.Syscall6(_glRenderbufferStorage.Addr(), 4, uintptr(target), uintptr(internalformat), uintptr(width), uintptr(height), 0, 0) -} -func (c *Functions) Scissor(x, y, width, height int32) { - syscall.Syscall6(_glScissor.Addr(), 4, uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0, 0) -} -func (c *Functions) ShaderSource(s gl.Shader, src string) { - var n uintptr = uintptr(len(src)) - psrc := &src - syscall.Syscall6(_glShaderSource.Addr(), 4, uintptr(s.V), 1, uintptr(unsafe.Pointer(psrc)), uintptr(unsafe.Pointer(&n)), 0, 0) - issue34474KeepAlive(psrc) -} -func (c *Functions) TexImage2D(target gl.Enum, level int, internalFormat int, width, height int, format, ty gl.Enum, data []byte) { - if len(data) == 0 { - syscall.Syscall9(_glTexImage2D.Addr(), 9, uintptr(target), uintptr(level), uintptr(internalFormat), uintptr(width), uintptr(height), 0, uintptr(format), uintptr(ty), 0) - } else { - d0 := &data[0] - syscall.Syscall9(_glTexImage2D.Addr(), 9, uintptr(target), uintptr(level), uintptr(internalFormat), uintptr(width), uintptr(height), 0, uintptr(format), uintptr(ty), uintptr(unsafe.Pointer(d0))) - issue34474KeepAlive(d0) - } -} -func (c *Functions) TexSubImage2D(target gl.Enum, level int, x, y, width, height int, format, ty gl.Enum, data []byte) { - 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))) - issue34474KeepAlive(d0) -} -func (c *Functions) TexParameteri(target, pname gl.Enum, param int) { - syscall.Syscall(_glTexParameteri.Addr(), 3, uintptr(target), uintptr(pname), uintptr(param)) -} -func (f *Functions) UniformBlockBinding(p gl.Program, uniformBlockIndex uint, uniformBlockBinding uint) { - syscall.Syscall(_glUniformBlockBinding.Addr(), 3, uintptr(p.V), uintptr(uniformBlockIndex), uintptr(uniformBlockBinding)) -} -func (c *Functions) Uniform1f(dst gl.Uniform, v float32) { - syscall.Syscall(_glUniform1f.Addr(), 2, uintptr(dst.V), uintptr(math.Float32bits(v)), 0) -} -func (c *Functions) Uniform1i(dst gl.Uniform, v int) { - syscall.Syscall(_glUniform1i.Addr(), 2, uintptr(dst.V), uintptr(v), 0) -} -func (c *Functions) Uniform2f(dst gl.Uniform, v0, v1 float32) { - syscall.Syscall(_glUniform2f.Addr(), 3, uintptr(dst.V), uintptr(math.Float32bits(v0)), uintptr(math.Float32bits(v1))) -} -func (c *Functions) Uniform3f(dst gl.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) -} -func (c *Functions) Uniform4f(dst gl.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) -} -func (c *Functions) UseProgram(p gl.Program) { - syscall.Syscall(_glUseProgram.Addr(), 1, uintptr(p.V), 0, 0) -} -func (c *Functions) VertexAttribPointer(dst gl.Attrib, size int, ty gl.Enum, normalized bool, stride, offset int) { - var norm uintptr - if normalized { - norm = 1 - } - 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) { - syscall.Syscall6(_glViewport.Addr(), 4, uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0, 0) -} - -func cString(s string) []byte { - b := make([]byte, len(s)+1) - copy(b, s) - return b -} - -// issue34474KeepAlive calls runtime.KeepAlive as a -// workaround for golang.org/issue/34474. -func issue34474KeepAlive(v interface{}) { - runtime.KeepAlive(v) -} diff --git a/vendor/gioui.org/app/internal/log/log_android.go b/vendor/gioui.org/app/internal/log/log_android.go index 7936911..1245598 100644 --- a/vendor/gioui.org/app/internal/log/log_android.go +++ b/vendor/gioui.org/app/internal/log/log_android.go @@ -40,16 +40,22 @@ type androidLogWriter struct { } func (w *androidLogWriter) Write(data []byte) (int, error) { - // Truncate the buffer, leaving space for the '\0'. - if max := len(w.buf) - 1; len(data) > max { - data = data[:max] + n := 0 + for len(data) > 0 { + msg := data + // Truncate the buffer, leaving space for the '\0'. + if max := len(w.buf) - 1; len(msg) > max { + msg = msg[:max] + } + buf := w.buf[:len(msg)+1] + copy(buf, msg) + // Terminating '\0'. + buf[len(msg)] = 0 + C.__android_log_write(C.ANDROID_LOG_INFO, logTag, (*C.char)(unsafe.Pointer(&buf[0]))) + n += len(msg) + data = data[len(msg):] } - buf := w.buf[:len(data)+1] - copy(buf, data) - // Terminating '\0'. - buf[len(data)] = 0 - C.__android_log_write(C.ANDROID_LOG_INFO, logTag, (*C.char)(unsafe.Pointer(&buf[0]))) - return len(data), nil + return n, nil } func logFd(fd uintptr) { diff --git a/vendor/gioui.org/app/internal/log/log_ios.go b/vendor/gioui.org/app/internal/log/log_ios.go index e49f425..cad4e19 100644 --- a/vendor/gioui.org/app/internal/log/log_ios.go +++ b/vendor/gioui.org/app/internal/log/log_ios.go @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Unlicense OR MIT +//go:build darwin && ios // +build darwin,ios package log @@ -7,7 +8,11 @@ package log /* #cgo CFLAGS: -Werror -fmodules -fobjc-arc -x objective-c -__attribute__ ((visibility ("hidden"))) void nslog(char *str); +@import Foundation; + +static void nslog(char *str) { + NSLog(@"%@", @(str)); +} */ import "C" @@ -17,7 +22,7 @@ import ( "log" "unsafe" - _ "gioui.org/app/internal/cocoainit" + _ "gioui.org/internal/cocoainit" ) func init() { diff --git a/vendor/gioui.org/app/internal/log/log_ios.m b/vendor/gioui.org/app/internal/log/log_ios.m deleted file mode 100644 index 201bc36..0000000 --- a/vendor/gioui.org/app/internal/log/log_ios.m +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -// +build darwin,ios - -@import Foundation; - -#include "_cgo_export.h" - -void nslog(char *str) { - NSLog(@"%@", @(str)); -} diff --git a/vendor/gioui.org/app/internal/srgb/srgb.go b/vendor/gioui.org/app/internal/srgb/srgb.go deleted file mode 100644 index c148b04..0000000 --- a/vendor/gioui.org/app/internal/srgb/srgb.go +++ /dev/null @@ -1,195 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package srgb - -import ( - "fmt" - "runtime" - "strings" - - "gioui.org/app/internal/glimpl" - "gioui.org/gpu/gl" - "gioui.org/internal/unsafe" -) - -// FBO implements an intermediate sRGB FBO -// for gamma-correct rendering on platforms without -// sRGB enabled native framebuffers. -type FBO struct { - c *glimpl.Functions - width, height int - frameBuffer gl.Framebuffer - depthBuffer gl.Renderbuffer - colorTex gl.Texture - blitted bool - quad gl.Buffer - prog gl.Program - gl3 bool -} - -func New(f *glimpl.Functions) (*FBO, error) { - var gl3 bool - glVer := f.GetString(gl.VERSION) - ver, _, err := gl.ParseGLVersion(glVer) - if err != nil { - return nil, err - } - if ver[0] >= 3 { - gl3 = true - } else { - exts := f.GetString(gl.EXTENSIONS) - if !strings.Contains(exts, "EXT_sRGB") { - return nil, fmt.Errorf("no support for OpenGL ES 3 nor EXT_sRGB") - } - } - s := &FBO{ - c: f, - gl3: gl3, - frameBuffer: f.CreateFramebuffer(), - colorTex: f.CreateTexture(), - depthBuffer: f.CreateRenderbuffer(), - } - f.BindTexture(gl.TEXTURE_2D, s.colorTex) - f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) - f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) - f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) - f.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) - return s, nil -} - -func (s *FBO) Blit() { - if !s.blitted { - prog, err := gl.CreateProgram(s.c, blitVSrc, blitFSrc, []string{"pos", "uv"}) - if err != nil { - panic(err) - } - s.prog = prog - s.c.UseProgram(prog) - s.c.Uniform1i(gl.GetUniformLocation(s.c, prog, "tex"), 0) - s.quad = s.c.CreateBuffer() - s.c.BindBuffer(gl.ARRAY_BUFFER, s.quad) - s.c.BufferData(gl.ARRAY_BUFFER, - unsafe.BytesView([]float32{ - -1, +1, 0, 1, - +1, +1, 1, 1, - -1, -1, 0, 0, - +1, -1, 1, 0, - }), - gl.STATIC_DRAW) - s.blitted = true - } - s.c.BindFramebuffer(gl.FRAMEBUFFER, gl.Framebuffer{}) - s.c.UseProgram(s.prog) - s.c.BindTexture(gl.TEXTURE_2D, s.colorTex) - s.c.BindBuffer(gl.ARRAY_BUFFER, s.quad) - s.c.VertexAttribPointer(0 /* pos */, 2, gl.FLOAT, false, 4*4, 0) - s.c.VertexAttribPointer(1 /* uv */, 2, gl.FLOAT, false, 4*4, 4*2) - s.c.EnableVertexAttribArray(0) - s.c.EnableVertexAttribArray(1) - s.c.DrawArrays(gl.TRIANGLE_STRIP, 0, 4) - s.c.BindTexture(gl.TEXTURE_2D, gl.Texture{}) - s.c.DisableVertexAttribArray(0) - s.c.DisableVertexAttribArray(1) - s.c.BindFramebuffer(gl.FRAMEBUFFER, s.frameBuffer) - s.c.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0) - s.c.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT) - // The Android emulator requires framebuffer 0 bound at eglSwapBuffer time. - // Bind the sRGB framebuffer again in afterPresent. - s.c.BindFramebuffer(gl.FRAMEBUFFER, gl.Framebuffer{}) -} - -func (s *FBO) AfterPresent() { - s.c.BindFramebuffer(gl.FRAMEBUFFER, s.frameBuffer) -} - -func (s *FBO) Refresh(w, h int) error { - s.width, s.height = w, h - if w == 0 || h == 0 { - return nil - } - s.c.BindTexture(gl.TEXTURE_2D, s.colorTex) - if s.gl3 { - s.c.TexImage2D(gl.TEXTURE_2D, 0, gl.SRGB8_ALPHA8, w, h, gl.RGBA, gl.UNSIGNED_BYTE, nil) - } else /* EXT_sRGB */ { - s.c.TexImage2D(gl.TEXTURE_2D, 0, gl.SRGB_ALPHA_EXT, w, h, gl.SRGB_ALPHA_EXT, gl.UNSIGNED_BYTE, nil) - } - currentRB := gl.Renderbuffer(s.c.GetBinding(gl.RENDERBUFFER_BINDING)) - s.c.BindRenderbuffer(gl.RENDERBUFFER, s.depthBuffer) - s.c.RenderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, w, h) - s.c.BindRenderbuffer(gl.RENDERBUFFER, currentRB) - s.c.BindFramebuffer(gl.FRAMEBUFFER, s.frameBuffer) - s.c.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, s.colorTex, 0) - s.c.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, s.depthBuffer) - if st := s.c.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE { - return fmt.Errorf("sRGB framebuffer incomplete (%dx%d), status: %#x error: %x", s.width, s.height, st, s.c.GetError()) - } - - if runtime.GOOS == "js" { - // With macOS Safari, rendering to and then reading from a SRGB8_ALPHA8 - // texture result in twice gamma corrected colors. Using a plain RGBA - // texture seems to work. - s.c.ClearColor(.5, .5, .5, 1.0) - s.c.Clear(gl.COLOR_BUFFER_BIT) - var pixel [4]byte - s.c.ReadPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel[:]) - if pixel[0] == 128 { // Correct sRGB color value is ~188 - s.c.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, w, h, gl.RGBA, gl.UNSIGNED_BYTE, nil) - if st := s.c.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE { - return fmt.Errorf("fallback RGBA framebuffer incomplete (%dx%d), status: %#x error: %x", s.width, s.height, st, s.c.GetError()) - } - } - } - - return nil -} - -func (s *FBO) Release() { - s.c.DeleteFramebuffer(s.frameBuffer) - s.c.DeleteTexture(s.colorTex) - s.c.DeleteRenderbuffer(s.depthBuffer) - if s.blitted { - s.c.DeleteBuffer(s.quad) - s.c.DeleteProgram(s.prog) - } - s.c = nil -} - -const ( - blitVSrc = ` -#version 100 - -precision highp float; - -attribute vec2 pos; -attribute vec2 uv; - -varying vec2 vUV; - -void main() { - gl_Position = vec4(pos, 0, 1); - vUV = uv; -} -` - blitFSrc = ` -#version 100 - -precision mediump float; - -uniform sampler2D tex; -varying vec2 vUV; - -vec3 gamma(vec3 rgb) { - vec3 exp = vec3(1.055)*pow(rgb, vec3(0.41666)) - vec3(0.055); - vec3 lin = rgb * vec3(12.92); - bvec3 cut = lessThan(rgb, vec3(0.0031308)); - return vec3(cut.r ? lin.r : exp.r, cut.g ? lin.g : exp.g, cut.b ? lin.b : exp.b); -} - -void main() { - vec4 col = texture2D(tex, vUV); - vec3 rgb = col.rgb; - rgb = gamma(rgb); - gl_FragColor = vec4(rgb, col.a); -} -` -) diff --git a/vendor/gioui.org/app/internal/window/GioView.java b/vendor/gioui.org/app/internal/window/GioView.java deleted file mode 100644 index be90c50..0000000 --- a/vendor/gioui.org/app/internal/window/GioView.java +++ /dev/null @@ -1,255 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package org.gioui; - -import java.lang.Class; -import java.lang.IllegalAccessException; -import java.lang.InstantiationException; -import java.lang.ExceptionInInitializerError; -import java.lang.SecurityException; -import android.app.Activity; -import android.app.Fragment; -import android.app.FragmentManager; -import android.app.FragmentTransaction; -import android.content.Context; -import android.graphics.Rect; -import android.os.Build; -import android.text.Editable; -import android.util.AttributeSet; -import android.util.TypedValue; -import android.view.Choreographer; -import android.view.KeyCharacterMap; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewConfiguration; -import android.view.WindowInsets; -import android.view.Surface; -import android.view.SurfaceView; -import android.view.SurfaceHolder; -import android.view.inputmethod.BaseInputConnection; -import android.view.inputmethod.InputConnection; -import android.view.inputmethod.InputMethodManager; -import android.view.inputmethod.EditorInfo; - -import java.io.UnsupportedEncodingException; - -public final class GioView extends SurfaceView implements Choreographer.FrameCallback { - private final static Object initLock = new Object(); - private static boolean jniLoaded; - - private final SurfaceHolder.Callback surfCallbacks; - private final View.OnFocusChangeListener focusCallback; - private final InputMethodManager imm; - private final float scrollXScale; - private final float scrollYScale; - - private long nhandle; - - public GioView(Context context) { - this(context, null); - } - - public GioView(Context context, AttributeSet attrs) { - super(context, attrs); - - // Late initialization of the Go runtime to wait for a valid context. - Gio.init(context.getApplicationContext()); - - ViewConfiguration conf = ViewConfiguration.get(context); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - scrollXScale = conf.getScaledHorizontalScrollFactor(); - scrollYScale = conf.getScaledVerticalScrollFactor(); - } else { - float listItemHeight = 48; // dp - float px = TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, - listItemHeight, - getResources().getDisplayMetrics() - ); - scrollXScale = px; - scrollYScale = px; - } - - nhandle = onCreateView(this); - imm = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE); - setFocusable(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() { - @Override public void surfaceCreated(SurfaceHolder holder) { - // Ignore; surfaceChanged is guaranteed to be called immediately after this. - } - @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - onSurfaceChanged(nhandle, getHolder().getSurface()); - } - @Override public void surfaceDestroyed(SurfaceHolder holder) { - onSurfaceDestroyed(nhandle); - } - }; - getHolder().addCallback(surfCallbacks); - } - - @Override public boolean onKeyDown(int keyCode, KeyEvent event) { - onKeyEvent(nhandle, keyCode, event.getUnicodeChar(), event.getEventTime()); - return false; - } - - @Override public boolean onGenericMotionEvent(MotionEvent event) { - dispatchMotionEvent(event); - return true; - } - - @Override public boolean onTouchEvent(MotionEvent event) { - // Ask for unbuffered events. Flutter and Chrome does it - // so I assume its good for us as well. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - requestUnbufferedDispatch(event); - } - - dispatchMotionEvent(event); - return true; - } - - private void dispatchMotionEvent(MotionEvent event) { - for (int j = 0; j < event.getHistorySize(); j++) { - long time = event.getHistoricalEventTime(j); - for (int i = 0; i < event.getPointerCount(); i++) { - onTouchEvent( - nhandle, - event.ACTION_MOVE, - event.getPointerId(i), - event.getToolType(i), - event.getHistoricalX(i, j), - event.getHistoricalY(i, j), - scrollXScale*event.getHistoricalAxisValue(MotionEvent.AXIS_HSCROLL, i, j), - scrollYScale*event.getHistoricalAxisValue(MotionEvent.AXIS_VSCROLL, i, j), - event.getButtonState(), - time); - } - } - int act = event.getActionMasked(); - int idx = event.getActionIndex(); - for (int i = 0; i < event.getPointerCount(); i++) { - int pact = event.ACTION_MOVE; - if (i == idx) { - pact = act; - } - onTouchEvent( - nhandle, - pact, - event.getPointerId(i), - event.getToolType(i), - event.getX(i), event.getY(i), - scrollXScale*event.getAxisValue(MotionEvent.AXIS_HSCROLL, i), - scrollYScale*event.getAxisValue(MotionEvent.AXIS_VSCROLL, i), - event.getButtonState(), - event.getEventTime()); - } - } - - @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { - return new InputConnection(this); - } - - void showTextInput() { - post(new Runnable() { - @Override public void run() { - GioView.this.requestFocus(); - imm.showSoftInput(GioView.this, 0); - } - }); - } - - void hideTextInput() { - post(new Runnable() { - @Override public void run() { - imm.hideSoftInputFromWindow(getWindowToken(), 0); - } - }); - } - - @Override protected boolean fitSystemWindows(Rect insets) { - onWindowInsets(nhandle, insets.top, insets.right, insets.bottom, insets.left); - return true; - } - - void postFrameCallback() { - Choreographer.getInstance().removeFrameCallback(this); - Choreographer.getInstance().postFrameCallback(this); - } - - @Override public void doFrame(long nanos) { - onFrameCallback(nhandle, nanos); - } - - int getDensity() { - return getResources().getDisplayMetrics().densityDpi; - } - - float getFontScale() { - return getResources().getConfiguration().fontScale; - } - - void start() { - onStartView(nhandle); - } - - void stop() { - onStopView(nhandle); - } - - void destroy() { - setOnFocusChangeListener(null); - getHolder().removeCallback(surfCallbacks); - onDestroyView(nhandle); - nhandle = 0; - } - - void configurationChanged() { - onConfigurationChanged(nhandle); - } - - void lowMemory() { - onLowMemory(); - } - - boolean backPressed() { - return onBack(nhandle); - } - - static private native long onCreateView(GioView view); - static private native void onDestroyView(long handle); - static private native void onStartView(long handle); - static private native void onStopView(long handle); - static private native void onSurfaceDestroyed(long handle); - static private native void onSurfaceChanged(long handle, Surface surface); - static private native void onConfigurationChanged(long handle); - static private native void onWindowInsets(long handle, int top, int right, int bottom, int left); - static private native void onLowMemory(); - static private native void onTouchEvent(long handle, int action, int pointerID, int tool, float x, float y, float scrollX, float scrollY, int buttons, long time); - static private native void onKeyEvent(long handle, int code, int character, long time); - static private native void onFrameCallback(long handle, long nanos); - static private native boolean onBack(long handle); - static private native void onFocusChange(long handle, boolean focus); - - private static class InputConnection extends BaseInputConnection { - private final Editable editable; - - InputConnection(View view) { - // Passing false enables "dummy mode", where the BaseInputConnection - // attempts to convert IME operations to key events. - super(view, false); - editable = Editable.Factory.getInstance().newEditable(""); - } - - @Override public Editable getEditable() { - return editable; - } - } -} diff --git a/vendor/gioui.org/app/internal/window/d3d11_windows.go b/vendor/gioui.org/app/internal/window/d3d11_windows.go deleted file mode 100644 index f1e06a6..0000000 --- a/vendor/gioui.org/app/internal/window/d3d11_windows.go +++ /dev/null @@ -1,102 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package window - -import ( - "gioui.org/app/internal/d3d11" - "gioui.org/gpu/backend" -) - -type d3d11Context struct { - win *window - swchain *d3d11.SwapChain - fbo *d3d11.Framebuffer - backend backend.Device - *d3d11.Device - width, height int -} - -func init() { - backends = append(backends, gpuAPI{ - priority: 1, - initializer: func(w *window) (Context, error) { - hwnd, _, _ := w.HWND() - dev, err := d3d11.NewDevice() - if err != nil { - return nil, err - } - swchain, err := dev.CreateSwapChain(hwnd) - if err != nil { - dev.Release() - return nil, err - } - return &d3d11Context{win: w, Device: dev, swchain: swchain}, nil - }, - }) -} - -func (c *d3d11Context) Backend() (backend.Device, error) { - backend, err := d3d11.NewBackend(c.Device) - if err != nil { - return nil, err - } - c.backend = backend - c.backend.BindFramebuffer(c.fbo) - return backend, nil -} - -func (c *d3d11Context) Present() error { - if err := c.swchain.Present(); err != nil { - if err, ok := err.(d3d11.ErrorCode); ok { - switch err.Code { - case d3d11.DXGI_STATUS_OCCLUDED: - // Ignore - return nil - case d3d11.DXGI_ERROR_DEVICE_RESET, d3d11.DXGI_ERROR_DEVICE_REMOVED, d3d11.D3DDDIERR_DEVICEREMOVED: - return ErrDeviceLost - } - } - } - return nil -} - -func (c *d3d11Context) MakeCurrent() error { - _, width, height := c.win.HWND() - if c.fbo != nil && width == c.width && height == c.height { - c.backend.BindFramebuffer(c.fbo) - return nil - } - if c.fbo != nil { - c.fbo.Release() - c.fbo = nil - } - if err := c.swchain.Resize(); err != nil { - return err - } - c.width = width - c.height = height - fbo, err := c.swchain.Framebuffer(c.Device) - if err != nil { - return err - } - c.fbo = fbo - if c.backend != nil { - c.backend.BindFramebuffer(c.fbo) - } - return nil -} - -func (c *d3d11Context) Lock() {} - -func (c *d3d11Context) Unlock() {} - -func (c *d3d11Context) Release() { - if c.fbo != nil { - c.fbo.Release() - } - c.swchain.Release() - c.Device.Release() - c.fbo = nil - c.swchain = nil - c.Device = nil -} diff --git a/vendor/gioui.org/app/internal/window/egl_android.go b/vendor/gioui.org/app/internal/window/egl_android.go deleted file mode 100644 index 19c8d63..0000000 --- a/vendor/gioui.org/app/internal/window/egl_android.go +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package window - -/* -#include <EGL/egl.h> -*/ -import "C" - -import ( - "unsafe" - - "gioui.org/app/internal/egl" -) - -type context struct { - win *window - *egl.Context -} - -func (w *window) NewContext() (Context, error) { - ctx, err := egl.NewContext(nil) - if err != nil { - return nil, err - } - return &context{win: w, Context: ctx}, nil -} - -func (c *context) Release() { - if c.Context != nil { - c.Context.Release() - c.Context = nil - } -} - -func (c *context) MakeCurrent() error { - c.Context.ReleaseSurface() - win, width, height := c.win.nativeWindow(c.Context.VisualID()) - if win == nil { - return nil - } - eglSurf := egl.NativeWindowType(unsafe.Pointer(win)) - if err := c.Context.CreateSurface(eglSurf, width, height); err != nil { - return err - } - if err := c.Context.MakeCurrent(); err != nil { - return err - } - return nil -} - -func (c *context) Lock() {} - -func (c *context) Unlock() {} diff --git a/vendor/gioui.org/app/internal/window/egl_x11.go b/vendor/gioui.org/app/internal/window/egl_x11.go deleted file mode 100644 index ffe69c6..0000000 --- a/vendor/gioui.org/app/internal/window/egl_x11.go +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -// +build linux,!android,!nox11 freebsd openbsd - -package window - -import ( - "unsafe" - - "gioui.org/app/internal/egl" -) - -type x11Context struct { - win *x11Window - *egl.Context -} - -func (w *x11Window) NewContext() (Context, error) { - disp := egl.NativeDisplayType(unsafe.Pointer(w.display())) - ctx, err := egl.NewContext(disp) - if err != nil { - return nil, err - } - return &x11Context{win: w, Context: ctx}, nil -} - -func (c *x11Context) Release() { - if c.Context != nil { - c.Context.Release() - c.Context = nil - } -} - -func (c *x11Context) MakeCurrent() 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) - return nil -} - -func (c *x11Context) Lock() {} - -func (c *x11Context) Unlock() {} diff --git a/vendor/gioui.org/app/internal/window/gl_js.go b/vendor/gioui.org/app/internal/window/gl_js.go deleted file mode 100644 index 0adbdf4..0000000 --- a/vendor/gioui.org/app/internal/window/gl_js.go +++ /dev/null @@ -1,98 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package window - -import ( - "errors" - "syscall/js" - - "gioui.org/app/internal/glimpl" - "gioui.org/app/internal/srgb" - "gioui.org/gpu/backend" - "gioui.org/gpu/gl" -) - -type context struct { - ctx js.Value - cnv js.Value - f *glimpl.Functions - srgbFBO *srgb.FBO -} - -func newContext(w *window) (*context, error) { - args := map[string]interface{}{ - // Enable low latency rendering. - // See https://developers.google.com/web/updates/2019/05/desynchronized. - "desynchronized": true, - "preserveDrawingBuffer": true, - } - version := 2 - ctx := w.cnv.Call("getContext", "webgl2", args) - if ctx.IsNull() { - version = 1 - ctx = w.cnv.Call("getContext", "webgl", args) - } - if ctx.IsNull() { - return nil, errors.New("app: webgl is not supported") - } - f := &glimpl.Functions{Ctx: ctx} - if err := f.Init(version); err != nil { - return nil, err - } - c := &context{ - ctx: ctx, - cnv: w.cnv, - f: f, - } - return c, nil -} - -func (c *context) Backend() (backend.Device, error) { - return gl.NewBackend(c.f) -} - -func (c *context) Release() { - if c.srgbFBO != nil { - c.srgbFBO.Release() - c.srgbFBO = nil - } -} - -func (c *context) Present() error { - if c.srgbFBO != nil { - c.srgbFBO.Blit() - } - if c.srgbFBO != nil { - c.srgbFBO.AfterPresent() - } - if c.ctx.Call("isContextLost").Bool() { - return errors.New("context lost") - } - return nil -} - -func (c *context) Lock() {} - -func (c *context) Unlock() {} - -func (c *context) MakeCurrent() error { - if c.srgbFBO == nil { - var err error - c.srgbFBO, err = srgb.New(c.f) - if err != nil { - c.Release() - c.srgbFBO = nil - return err - } - } - w, h := c.cnv.Get("width").Int(), c.cnv.Get("height").Int() - if err := c.srgbFBO.Refresh(w, h); err != nil { - c.Release() - return err - } - return nil -} - -func (w *window) NewContext() (Context, error) { - return newContext(w) -} diff --git a/vendor/gioui.org/app/internal/window/gl_macos.go b/vendor/gioui.org/app/internal/window/gl_macos.go deleted file mode 100644 index e4e293d..0000000 --- a/vendor/gioui.org/app/internal/window/gl_macos.go +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -// +build darwin,!ios - -package window - -import ( - "gioui.org/app/internal/glimpl" - "gioui.org/gpu/backend" - "gioui.org/gpu/gl" -) - -/* -#include <CoreFoundation/CoreFoundation.h> -#include <CoreGraphics/CoreGraphics.h> -#include <AppKit/AppKit.h> -#include <OpenGL/gl3.h> - -__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createGLView(void); -__attribute__ ((visibility ("hidden"))) CFTypeRef gio_contextForView(CFTypeRef viewRef); -__attribute__ ((visibility ("hidden"))) void gio_makeCurrentContext(CFTypeRef ctx); -__attribute__ ((visibility ("hidden"))) void gio_flushContextBuffer(CFTypeRef ctx); -__attribute__ ((visibility ("hidden"))) void gio_clearCurrentContext(void); -__attribute__ ((visibility ("hidden"))) void gio_lockContext(CFTypeRef ctxRef); -__attribute__ ((visibility ("hidden"))) void gio_unlockContext(CFTypeRef ctxRef); -*/ -import "C" - -type context struct { - c *glimpl.Functions - ctx C.CFTypeRef - view C.CFTypeRef -} - -func init() { - viewFactory = func() C.CFTypeRef { - return C.gio_createGLView() - } -} - -func newContext(w *window) (*context, error) { - view := w.contextView() - ctx := C.gio_contextForView(view) - c := &context{ - ctx: ctx, - c: new(glimpl.Functions), - view: view, - } - return c, nil -} - -func (c *context) Backend() (backend.Device, error) { - return gl.NewBackend(c.c) -} - -func (c *context) Release() { - c.Lock() - defer c.Unlock() - C.gio_clearCurrentContext() - // We could release the context with [view clearGLContext] - // and rely on [view openGLContext] auto-creating a new context. - // However that second context is not properly set up by - // OpenGLContextView, so we'll stay on the safe side and keep - // the first context around. -} - -func (c *context) Present() error { - // Assume the caller already locked the context. - C.glFlush() - return nil -} - -func (c *context) Lock() { - C.gio_lockContext(c.ctx) -} - -func (c *context) Unlock() { - C.gio_unlockContext(c.ctx) -} - -func (c *context) MakeCurrent() error { - c.Lock() - defer c.Unlock() - C.gio_makeCurrentContext(c.ctx) - return nil -} - -func (w *window) NewContext() (Context, error) { - return newContext(w) -} diff --git a/vendor/gioui.org/app/internal/window/gl_macos.m b/vendor/gioui.org/app/internal/window/gl_macos.m deleted file mode 100644 index 0d7f772..0000000 --- a/vendor/gioui.org/app/internal/window/gl_macos.m +++ /dev/null @@ -1,143 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -// +build darwin,!ios - -@import AppKit; - -#include <CoreFoundation/CoreFoundation.h> -#include <OpenGL/OpenGL.h> -#include <OpenGL/gl3.h> -#include "_cgo_export.h" - -static void handleMouse(NSView *view, NSEvent *event, int typ, CGFloat dx, CGFloat dy) { - NSPoint p = [view convertPoint:[event locationInWindow] fromView:nil]; - if (!event.hasPreciseScrollingDeltas) { - // dx and dy are in rows and columns. - dx *= 10; - dy *= 10; - } - gio_onMouse((__bridge CFTypeRef)view, typ, [NSEvent pressedMouseButtons], p.x, p.y, dx, dy, [event timestamp], [event modifierFlags]); -} - -@interface GioView : NSOpenGLView -@end - -@implementation GioView -- (instancetype)initWithFrame:(NSRect)frameRect - pixelFormat:(NSOpenGLPixelFormat *)format { - return [super initWithFrame:frameRect pixelFormat:format]; -} -- (void)prepareOpenGL { - [super prepareOpenGL]; - // Bind a default VBA to emulate OpenGL ES 2. - GLuint defVBA; - glGenVertexArrays(1, &defVBA); - glBindVertexArray(defVBA); - glEnable(GL_FRAMEBUFFER_SRGB); -} -- (BOOL)isFlipped { - return YES; -} -- (void)update { - [super update]; - [self setNeedsDisplay:YES]; -} -- (void)drawRect:(NSRect)r { - gio_onDraw((__bridge CFTypeRef)self); -} -- (void)mouseDown:(NSEvent *)event { - handleMouse(self, event, GIO_MOUSE_DOWN, 0, 0); -} -- (void)mouseUp:(NSEvent *)event { - handleMouse(self, event, GIO_MOUSE_UP, 0, 0); -} -- (void)middleMouseDown:(NSEvent *)event { - handleMouse(self, event, GIO_MOUSE_DOWN, 0, 0); -} -- (void)middletMouseUp:(NSEvent *)event { - handleMouse(self, event, GIO_MOUSE_UP, 0, 0); -} -- (void)rightMouseDown:(NSEvent *)event { - handleMouse(self, event, GIO_MOUSE_DOWN, 0, 0); -} -- (void)rightMouseUp:(NSEvent *)event { - handleMouse(self, event, GIO_MOUSE_UP, 0, 0); -} -- (void)mouseMoved:(NSEvent *)event { - handleMouse(self, event, GIO_MOUSE_MOVE, 0, 0); -} -- (void)mouseDragged:(NSEvent *)event { - handleMouse(self, event, GIO_MOUSE_MOVE, 0, 0); -} -- (void)scrollWheel:(NSEvent *)event { - CGFloat dx = -event.scrollingDeltaX; - CGFloat dy = -event.scrollingDeltaY; - handleMouse(self, event, GIO_MOUSE_SCROLL, dx, dy); -} -- (void)keyDown:(NSEvent *)event { - NSString *keys = [event charactersIgnoringModifiers]; - gio_onKeys((__bridge CFTypeRef)self, (char *)[keys UTF8String], [event timestamp], [event modifierFlags]); - [self interpretKeyEvents:[NSArray arrayWithObject:event]]; -} -- (void)insertText:(id)string { - const char *utf8 = [string UTF8String]; - gio_onText((__bridge CFTypeRef)self, (char *)utf8); -} -- (void)doCommandBySelector:(SEL)sel { - // Don't pass commands up the responder chain. - // They will end up in a beep. -} -@end - -CFTypeRef gio_createGLView(void) { - @autoreleasepool { - NSOpenGLPixelFormatAttribute attr[] = { - NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, - NSOpenGLPFAColorSize, 24, - NSOpenGLPFADepthSize, 16, - NSOpenGLPFAAccelerated, - // Opt-in to automatic GPU switching. CGL-only property. - kCGLPFASupportsAutomaticGraphicsSwitching, - NSOpenGLPFAAllowOfflineRenderers, - 0 - }; - id pixFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr]; - - NSRect frame = NSMakeRect(0, 0, 0, 0); - GioView* view = [[GioView alloc] initWithFrame:frame pixelFormat:pixFormat]; - - [view setWantsBestResolutionOpenGLSurface:YES]; - [view setWantsLayer:YES]; // The default in Mojave. - - return CFBridgingRetain(view); - } -} - -void gio_setNeedsDisplay(CFTypeRef viewRef) { - NSOpenGLView *view = (__bridge NSOpenGLView *)viewRef; - [view setNeedsDisplay:YES]; -} - -CFTypeRef gio_contextForView(CFTypeRef viewRef) { - NSOpenGLView *view = (__bridge NSOpenGLView *)viewRef; - return (__bridge CFTypeRef)view.openGLContext; -} - -void gio_clearCurrentContext(void) { - [NSOpenGLContext clearCurrentContext]; -} - -void gio_makeCurrentContext(CFTypeRef ctxRef) { - NSOpenGLContext *ctx = (__bridge NSOpenGLContext *)ctxRef; - [ctx makeCurrentContext]; -} - -void gio_lockContext(CFTypeRef ctxRef) { - NSOpenGLContext *ctx = (__bridge NSOpenGLContext *)ctxRef; - CGLLockContext([ctx CGLContextObj]); -} - -void gio_unlockContext(CFTypeRef ctxRef) { - NSOpenGLContext *ctx = (__bridge NSOpenGLContext *)ctxRef; - CGLUnlockContext([ctx CGLContextObj]); -} diff --git a/vendor/gioui.org/app/internal/window/os_android.c b/vendor/gioui.org/app/internal/window/os_android.c deleted file mode 100644 index 8a2c62d..0000000 --- a/vendor/gioui.org/app/internal/window/os_android.c +++ /dev/null @@ -1,96 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -#include <jni.h> -#include "_cgo_export.h" - -jint gio_jni_GetEnv(JavaVM *vm, JNIEnv **env, jint version) { - return (*vm)->GetEnv(vm, (void **)env, version); -} - -jint gio_jni_GetJavaVM(JNIEnv *env, JavaVM **jvm) { - return (*env)->GetJavaVM(env, jvm); -} - -jint gio_jni_AttachCurrentThread(JavaVM *vm, JNIEnv **p_env, void *thr_args) { - return (*vm)->AttachCurrentThread(vm, p_env, thr_args); -} - -jint gio_jni_DetachCurrentThread(JavaVM *vm) { - return (*vm)->DetachCurrentThread(vm); -} - -jobject gio_jni_NewGlobalRef(JNIEnv *env, jobject obj) { - return (*env)->NewGlobalRef(env, obj); -} - -void gio_jni_DeleteGlobalRef(JNIEnv *env, jobject obj) { - (*env)->DeleteGlobalRef(env, obj); -} - -jclass gio_jni_GetObjectClass(JNIEnv *env, jobject obj) { - return (*env)->GetObjectClass(env, obj); -} - -jmethodID gio_jni_GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig) { - return (*env)->GetMethodID(env, clazz, name, sig); -} - -jmethodID gio_jni_GetStaticMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig) { - return (*env)->GetStaticMethodID(env, clazz, name, sig); -} - -jfloat gio_jni_CallFloatMethod(JNIEnv *env, jobject obj, jmethodID methodID) { - return (*env)->CallFloatMethod(env, obj, methodID); -} - -jint gio_jni_CallIntMethod(JNIEnv *env, jobject obj, jmethodID methodID) { - return (*env)->CallIntMethod(env, obj, methodID); -} - -void gio_jni_CallStaticVoidMethodA(JNIEnv *env, jclass cls, jmethodID methodID, const jvalue *args) { - (*env)->CallStaticVoidMethodA(env, cls, methodID, args); -} - -void gio_jni_CallVoidMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args) { - (*env)->CallVoidMethodA(env, obj, methodID, args); -} - -jbyte *gio_jni_GetByteArrayElements(JNIEnv *env, jbyteArray arr) { - return (*env)->GetByteArrayElements(env, arr, NULL); -} - -void gio_jni_ReleaseByteArrayElements(JNIEnv *env, jbyteArray arr, jbyte *bytes) { - (*env)->ReleaseByteArrayElements(env, arr, bytes, JNI_ABORT); -} - -jsize gio_jni_GetArrayLength(JNIEnv *env, jbyteArray arr) { - return (*env)->GetArrayLength(env, arr); -} - -jstring gio_jni_NewString(JNIEnv *env, const jchar *unicodeChars, jsize len) { - return (*env)->NewString(env, unicodeChars, len); -} - -jsize gio_jni_GetStringLength(JNIEnv *env, jstring str) { - return (*env)->GetStringLength(env, str); -} - -const jchar *gio_jni_GetStringChars(JNIEnv *env, jstring str) { - return (*env)->GetStringChars(env, str, NULL); -} - -jthrowable gio_jni_ExceptionOccurred(JNIEnv *env) { - return (*env)->ExceptionOccurred(env); -} - -void gio_jni_ExceptionClear(JNIEnv *env) { - (*env)->ExceptionClear(env); -} - -jobject gio_jni_CallObjectMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args) { - return (*env)->CallObjectMethodA(env, obj, method, args); -} - -jobject gio_jni_CallStaticObjectMethodA(JNIEnv *env, jclass cls, jmethodID method, jvalue *args) { - return (*env)->CallStaticObjectMethodA(env, cls, method, args); -} diff --git a/vendor/gioui.org/app/internal/window/os_android.go b/vendor/gioui.org/app/internal/window/os_android.go deleted file mode 100644 index 3458b26..0000000 --- a/vendor/gioui.org/app/internal/window/os_android.go +++ /dev/null @@ -1,684 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package window - -/* -#cgo CFLAGS: -Werror -#cgo LDFLAGS: -landroid - -#include <android/native_window_jni.h> -#include <android/configuration.h> -#include <android/keycodes.h> -#include <android/input.h> -#include <stdlib.h> - -__attribute__ ((visibility ("hidden"))) jint gio_jni_GetEnv(JavaVM *vm, JNIEnv **env, jint version); -__attribute__ ((visibility ("hidden"))) jint gio_jni_GetJavaVM(JNIEnv *env, JavaVM **jvm); -__attribute__ ((visibility ("hidden"))) jint gio_jni_AttachCurrentThread(JavaVM *vm, JNIEnv **p_env, void *thr_args); -__attribute__ ((visibility ("hidden"))) jint gio_jni_DetachCurrentThread(JavaVM *vm); - -__attribute__ ((visibility ("hidden"))) jobject gio_jni_NewGlobalRef(JNIEnv *env, jobject obj); -__attribute__ ((visibility ("hidden"))) void gio_jni_DeleteGlobalRef(JNIEnv *env, jobject obj); -__attribute__ ((visibility ("hidden"))) jclass gio_jni_GetObjectClass(JNIEnv *env, jobject obj); -__attribute__ ((visibility ("hidden"))) jmethodID gio_jni_GetStaticMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig); -__attribute__ ((visibility ("hidden"))) jmethodID gio_jni_GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig); -__attribute__ ((visibility ("hidden"))) jfloat gio_jni_CallFloatMethod(JNIEnv *env, jobject obj, jmethodID methodID); -__attribute__ ((visibility ("hidden"))) jint gio_jni_CallIntMethod(JNIEnv *env, jobject obj, jmethodID methodID); -__attribute__ ((visibility ("hidden"))) void gio_jni_CallStaticVoidMethodA(JNIEnv *env, jclass cls, jmethodID methodID, const jvalue *args); -__attribute__ ((visibility ("hidden"))) void gio_jni_CallVoidMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); -__attribute__ ((visibility ("hidden"))) jbyte *gio_jni_GetByteArrayElements(JNIEnv *env, jbyteArray arr); -__attribute__ ((visibility ("hidden"))) void gio_jni_ReleaseByteArrayElements(JNIEnv *env, jbyteArray arr, jbyte *bytes); -__attribute__ ((visibility ("hidden"))) jsize gio_jni_GetArrayLength(JNIEnv *env, jbyteArray arr); -__attribute__ ((visibility ("hidden"))) jstring gio_jni_NewString(JNIEnv *env, const jchar *unicodeChars, jsize len); -__attribute__ ((visibility ("hidden"))) jsize gio_jni_GetStringLength(JNIEnv *env, jstring str); -__attribute__ ((visibility ("hidden"))) const jchar *gio_jni_GetStringChars(JNIEnv *env, jstring str); -__attribute__ ((visibility ("hidden"))) jthrowable gio_jni_ExceptionOccurred(JNIEnv *env); -__attribute__ ((visibility ("hidden"))) void gio_jni_ExceptionClear(JNIEnv *env); -__attribute__ ((visibility ("hidden"))) jobject gio_jni_CallObjectMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args); -__attribute__ ((visibility ("hidden"))) jobject gio_jni_CallStaticObjectMethodA(JNIEnv *env, jclass cls, jmethodID method, jvalue *args); -*/ -import "C" - -import ( - "errors" - "fmt" - "image" - "reflect" - "runtime" - "runtime/debug" - "sync" - "time" - "unicode/utf16" - "unsafe" - - "gioui.org/f32" - "gioui.org/io/key" - "gioui.org/io/pointer" - "gioui.org/io/system" - "gioui.org/unit" -) - -type window struct { - callbacks Callbacks - - view C.jobject - - dpi int - fontScale float32 - insets system.Insets - - stage system.Stage - started bool - - mu sync.Mutex - win *C.ANativeWindow - animating bool - - // Cached Java methods. - mgetDensity C.jmethodID - mgetFontScale C.jmethodID - mshowTextInput C.jmethodID - mhideTextInput C.jmethodID - mpostFrameCallback C.jmethodID -} - -type jvalue uint64 // The largest JNI type fits in 64 bits. - -var dataDirChan = make(chan string, 1) - -var android struct { - // mu protects all fields of this structure. However, once a - // non-nil jvm is returned from javaVM, all the other fields may - // be accessed unlocked. - mu sync.Mutex - jvm *C.JavaVM - - // appCtx is the global Android App context. - appCtx C.jobject - // gioCls is the class of the Gio class. - gioCls C.jclass - - mwriteClipboard C.jmethodID - mreadClipboard C.jmethodID - mwakeupMainThread C.jmethodID -} - -var views = make(map[C.jlong]*window) - -var mainWindow = newWindowRendezvous() - -var mainFuncs = make(chan func(env *C.JNIEnv), 1) - -func getMethodID(env *C.JNIEnv, class C.jclass, method, sig string) C.jmethodID { - m := C.CString(method) - defer C.free(unsafe.Pointer(m)) - s := C.CString(sig) - defer C.free(unsafe.Pointer(s)) - jm := C.gio_jni_GetMethodID(env, class, m, s) - if err := exception(env); err != nil { - panic(err) - } - return jm -} - -func getStaticMethodID(env *C.JNIEnv, class C.jclass, method, sig string) C.jmethodID { - m := C.CString(method) - defer C.free(unsafe.Pointer(m)) - s := C.CString(sig) - defer C.free(unsafe.Pointer(s)) - jm := C.gio_jni_GetStaticMethodID(env, class, m, s) - if err := exception(env); err != nil { - panic(err) - } - return jm -} - -//export Java_org_gioui_Gio_runGoMain -func Java_org_gioui_Gio_runGoMain(env *C.JNIEnv, class C.jclass, jdataDir C.jbyteArray, context C.jobject) { - initJVM(env, class, context) - dirBytes := C.gio_jni_GetByteArrayElements(env, jdataDir) - if dirBytes == nil { - panic("runGoMain: GetByteArrayElements failed") - } - n := C.gio_jni_GetArrayLength(env, jdataDir) - dataDir := C.GoStringN((*C.char)(unsafe.Pointer(dirBytes)), n) - dataDirChan <- dataDir - C.gio_jni_ReleaseByteArrayElements(env, jdataDir, dirBytes) - - runMain() -} - -func initJVM(env *C.JNIEnv, gio C.jclass, ctx C.jobject) { - android.mu.Lock() - defer android.mu.Unlock() - if res := C.gio_jni_GetJavaVM(env, &android.jvm); res != 0 { - panic("gio: GetJavaVM failed") - } - android.appCtx = C.gio_jni_NewGlobalRef(env, ctx) - android.gioCls = C.jclass(C.gio_jni_NewGlobalRef(env, C.jobject(gio))) - android.mwriteClipboard = getStaticMethodID(env, gio, "writeClipboard", "(Landroid/content/Context;Ljava/lang/String;)V") - android.mreadClipboard = getStaticMethodID(env, gio, "readClipboard", "(Landroid/content/Context;)Ljava/lang/String;") - android.mwakeupMainThread = getStaticMethodID(env, gio, "wakeupMainThread", "()V") -} - -func JavaVM() uintptr { - jvm := javaVM() - return uintptr(unsafe.Pointer(jvm)) -} - -func javaVM() *C.JavaVM { - android.mu.Lock() - defer android.mu.Unlock() - return android.jvm -} - -func AppContext() uintptr { - android.mu.Lock() - defer android.mu.Unlock() - return uintptr(android.appCtx) -} - -func GetDataDir() string { - return <-dataDirChan -} - -//export Java_org_gioui_GioView_onCreateView -func Java_org_gioui_GioView_onCreateView(env *C.JNIEnv, class C.jclass, view C.jobject) C.jlong { - view = C.gio_jni_NewGlobalRef(env, view) - w := &window{ - view: view, - mgetDensity: getMethodID(env, class, "getDensity", "()I"), - mgetFontScale: getMethodID(env, class, "getFontScale", "()F"), - mshowTextInput: getMethodID(env, class, "showTextInput", "()V"), - mhideTextInput: getMethodID(env, class, "hideTextInput", "()V"), - mpostFrameCallback: getMethodID(env, class, "postFrameCallback", "()V"), - } - wopts := <-mainWindow.out - w.callbacks = wopts.window - w.callbacks.SetDriver(w) - handle := C.jlong(view) - views[handle] = w - w.loadConfig(env, class) - w.setStage(system.StagePaused) - return handle -} - -//export Java_org_gioui_GioView_onDestroyView -func Java_org_gioui_GioView_onDestroyView(env *C.JNIEnv, class C.jclass, handle C.jlong) { - w := views[handle] - w.callbacks.SetDriver(nil) - delete(views, handle) - C.gio_jni_DeleteGlobalRef(env, w.view) - w.view = 0 -} - -//export Java_org_gioui_GioView_onStopView -func Java_org_gioui_GioView_onStopView(env *C.JNIEnv, class C.jclass, handle C.jlong) { - w := views[handle] - w.started = false - w.setStage(system.StagePaused) -} - -//export Java_org_gioui_GioView_onStartView -func Java_org_gioui_GioView_onStartView(env *C.JNIEnv, class C.jclass, handle C.jlong) { - w := views[handle] - w.started = true - if w.aNativeWindow() != nil { - w.setVisible() - } -} - -//export Java_org_gioui_GioView_onSurfaceDestroyed -func Java_org_gioui_GioView_onSurfaceDestroyed(env *C.JNIEnv, class C.jclass, handle C.jlong) { - w := views[handle] - w.mu.Lock() - w.win = nil - w.mu.Unlock() - w.setStage(system.StagePaused) -} - -//export Java_org_gioui_GioView_onSurfaceChanged -func Java_org_gioui_GioView_onSurfaceChanged(env *C.JNIEnv, class C.jclass, handle C.jlong, surf C.jobject) { - w := views[handle] - w.mu.Lock() - w.win = C.ANativeWindow_fromSurface(env, surf) - w.mu.Unlock() - if w.started { - w.setVisible() - } -} - -//export Java_org_gioui_GioView_onLowMemory -func Java_org_gioui_GioView_onLowMemory() { - runtime.GC() - debug.FreeOSMemory() -} - -//export Java_org_gioui_GioView_onConfigurationChanged -func Java_org_gioui_GioView_onConfigurationChanged(env *C.JNIEnv, class C.jclass, view C.jlong) { - w := views[view] - w.loadConfig(env, class) - if w.stage >= system.StageRunning { - w.draw(true) - } -} - -//export Java_org_gioui_GioView_onFrameCallback -func Java_org_gioui_GioView_onFrameCallback(env *C.JNIEnv, class C.jclass, view C.jlong, nanos C.jlong) { - w, exist := views[view] - if !exist { - return - } - if w.stage < system.StageRunning { - return - } - w.mu.Lock() - anim := w.animating - w.mu.Unlock() - if anim { - runInJVM(javaVM(), func(env *C.JNIEnv) { - callVoidMethod(env, w.view, w.mpostFrameCallback) - }) - w.draw(false) - } -} - -//export Java_org_gioui_GioView_onBack -func Java_org_gioui_GioView_onBack(env *C.JNIEnv, class C.jclass, view C.jlong) C.jboolean { - w := views[view] - ev := &system.CommandEvent{Type: system.CommandBack} - w.callbacks.Event(ev) - if ev.Cancel { - return C.JNI_TRUE - } - return C.JNI_FALSE -} - -//export Java_org_gioui_GioView_onFocusChange -func Java_org_gioui_GioView_onFocusChange(env *C.JNIEnv, class C.jclass, view C.jlong, focus C.jboolean) { - w := views[view] - w.callbacks.Event(key.FocusEvent{Focus: focus == C.JNI_TRUE}) -} - -//export Java_org_gioui_GioView_onWindowInsets -func Java_org_gioui_GioView_onWindowInsets(env *C.JNIEnv, class C.jclass, view C.jlong, top, right, bottom, left C.jint) { - w := views[view] - w.insets = system.Insets{ - Top: unit.Px(float32(top)), - Right: unit.Px(float32(right)), - Bottom: unit.Px(float32(bottom)), - Left: unit.Px(float32(left)), - } - if w.stage >= system.StageRunning { - w.draw(true) - } -} - -func (w *window) setVisible() { - win := w.aNativeWindow() - width, height := C.ANativeWindow_getWidth(win), C.ANativeWindow_getHeight(win) - if width == 0 || height == 0 { - return - } - w.setStage(system.StageRunning) - w.draw(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) nativeWindow(visID int) (*C.ANativeWindow, int, int) { - win := w.aNativeWindow() - var width, height int - if win != nil { - if C.ANativeWindow_setBuffersGeometry(win, 0, 0, C.int32_t(visID)) != 0 { - panic(errors.New("ANativeWindow_setBuffersGeometry failed")) - } - w, h := C.ANativeWindow_getWidth(win), C.ANativeWindow_getHeight(win) - width, height = int(w), int(h) - } - return win, width, height -} - -func (w *window) aNativeWindow() *C.ANativeWindow { - w.mu.Lock() - defer w.mu.Unlock() - return w.win -} - -func (w *window) loadConfig(env *C.JNIEnv, class C.jclass) { - dpi := int(C.gio_jni_CallIntMethod(env, w.view, w.mgetDensity)) - w.fontScale = float32(C.gio_jni_CallFloatMethod(env, w.view, w.mgetFontScale)) - switch dpi { - case C.ACONFIGURATION_DENSITY_NONE, - C.ACONFIGURATION_DENSITY_DEFAULT, - C.ACONFIGURATION_DENSITY_ANY: - // Assume standard density. - w.dpi = C.ACONFIGURATION_DENSITY_MEDIUM - default: - w.dpi = int(dpi) - } -} - -func (w *window) SetAnimating(anim bool) { - w.mu.Lock() - w.animating = anim - w.mu.Unlock() - if anim { - runOnMain(func(env *C.JNIEnv) { - if w.view == 0 { - // View was destroyed while switching to main thread. - return - } - callVoidMethod(env, w.view, w.mpostFrameCallback) - }) - } -} - -func (w *window) draw(sync bool) { - win := w.aNativeWindow() - width, height := C.ANativeWindow_getWidth(win), C.ANativeWindow_getHeight(win) - if width == 0 || height == 0 { - return - } - const inchPrDp = 1.0 / 160 - ppdp := float32(w.dpi) * inchPrDp - w.callbacks.Event(FrameEvent{ - FrameEvent: system.FrameEvent{ - Now: time.Now(), - Size: image.Point{ - X: int(width), - Y: int(height), - }, - Insets: w.insets, - Metric: unit.Metric{ - PxPerDp: ppdp, - PxPerSp: w.fontScale * ppdp, - }, - }, - Sync: sync, - }) -} - -type keyMapper func(devId, keyCode C.int32_t) rune - -func runInJVM(jvm *C.JavaVM, f func(env *C.JNIEnv)) { - if jvm == nil { - panic("nil JVM") - } - runtime.LockOSThread() - defer runtime.UnlockOSThread() - var env *C.JNIEnv - if res := C.gio_jni_GetEnv(jvm, &env, C.JNI_VERSION_1_6); res != C.JNI_OK { - if res != C.JNI_EDETACHED { - panic(fmt.Errorf("JNI GetEnv failed with error %d", res)) - } - if C.gio_jni_AttachCurrentThread(jvm, &env, nil) != C.JNI_OK { - panic(errors.New("runInJVM: AttachCurrentThread failed")) - } - defer C.gio_jni_DetachCurrentThread(jvm) - } - - f(env) -} - -func convertKeyCode(code C.jint) (string, bool) { - var n string - switch code { - case C.AKEYCODE_DPAD_UP: - n = key.NameUpArrow - case C.AKEYCODE_DPAD_DOWN: - n = key.NameDownArrow - case C.AKEYCODE_DPAD_LEFT: - n = key.NameLeftArrow - case C.AKEYCODE_DPAD_RIGHT: - n = key.NameRightArrow - case C.AKEYCODE_FORWARD_DEL: - n = key.NameDeleteForward - case C.AKEYCODE_DEL: - n = key.NameDeleteBackward - default: - return "", false - } - return n, true -} - -//export Java_org_gioui_GioView_onKeyEvent -func Java_org_gioui_GioView_onKeyEvent(env *C.JNIEnv, class C.jclass, handle C.jlong, keyCode, r C.jint, t C.jlong) { - w := views[handle] - if n, ok := convertKeyCode(keyCode); ok { - w.callbacks.Event(key.Event{Name: n}) - } - if r != 0 { - w.callbacks.Event(key.EditEvent{Text: string(rune(r))}) - } -} - -//export Java_org_gioui_GioView_onTouchEvent -func Java_org_gioui_GioView_onTouchEvent(env *C.JNIEnv, class C.jclass, handle C.jlong, action, pointerID, tool C.jint, x, y, scrollX, scrollY C.jfloat, jbtns C.jint, t C.jlong) { - w := views[handle] - var typ pointer.Type - switch action { - case C.AMOTION_EVENT_ACTION_DOWN, C.AMOTION_EVENT_ACTION_POINTER_DOWN: - typ = pointer.Press - case C.AMOTION_EVENT_ACTION_UP, C.AMOTION_EVENT_ACTION_POINTER_UP: - typ = pointer.Release - case C.AMOTION_EVENT_ACTION_CANCEL: - typ = pointer.Cancel - case C.AMOTION_EVENT_ACTION_MOVE: - typ = pointer.Move - case C.AMOTION_EVENT_ACTION_SCROLL: - typ = pointer.Scroll - default: - return - } - var src pointer.Source - var btns pointer.Buttons - if jbtns&C.AMOTION_EVENT_BUTTON_PRIMARY != 0 { - btns |= pointer.ButtonLeft - } - if jbtns&C.AMOTION_EVENT_BUTTON_SECONDARY != 0 { - btns |= pointer.ButtonRight - } - if jbtns&C.AMOTION_EVENT_BUTTON_TERTIARY != 0 { - btns |= pointer.ButtonMiddle - } - switch tool { - case C.AMOTION_EVENT_TOOL_TYPE_FINGER: - src = pointer.Touch - case C.AMOTION_EVENT_TOOL_TYPE_MOUSE: - src = pointer.Mouse - case C.AMOTION_EVENT_TOOL_TYPE_UNKNOWN: - // For example, triggered via 'adb shell input tap'. - // Instead of discarding it, treat it as a touch event. - src = pointer.Touch - default: - return - } - w.callbacks.Event(pointer.Event{ - Type: typ, - Source: src, - Buttons: btns, - PointerID: pointer.ID(pointerID), - Time: time.Duration(t) * time.Millisecond, - Position: f32.Point{X: float32(x), Y: float32(y)}, - Scroll: f32.Pt(float32(scrollX), float32(scrollY)), - }) -} - -func (w *window) ShowTextInput(show bool) { - if w.view == 0 { - return - } - runInJVM(javaVM(), func(env *C.JNIEnv) { - if show { - callVoidMethod(env, w.view, w.mshowTextInput) - } else { - callVoidMethod(env, w.view, w.mhideTextInput) - } - }) -} - -func javaString(env *C.JNIEnv, str string) C.jstring { - if str == "" { - return 0 - } - utf16Chars := utf16.Encode([]rune(str)) - return C.gio_jni_NewString(env, (*C.jchar)(unsafe.Pointer(&utf16Chars[0])), C.int(len(utf16Chars))) -} - -// Do invokes the function with a global JNI handle to the view. If -// the view is destroyed, Do returns false and does not invoke the -// function. -// -// NOTE: Do must be invoked on the Android main thread. -func (w *window) Do(f func(view uintptr)) bool { - if w.view == 0 { - return false - } - runInJVM(javaVM(), func(env *C.JNIEnv) { - view := C.gio_jni_NewGlobalRef(env, w.view) - defer C.gio_jni_DeleteGlobalRef(env, view) - f(uintptr(view)) - }) - return true -} - -func varArgs(args []jvalue) *C.jvalue { - if len(args) == 0 { - return nil - } - return (*C.jvalue)(unsafe.Pointer(&args[0])) -} - -func callStaticVoidMethod(env *C.JNIEnv, cls C.jclass, method C.jmethodID, args ...jvalue) error { - C.gio_jni_CallStaticVoidMethodA(env, cls, method, varArgs(args)) - return exception(env) -} - -func callStaticObjectMethod(env *C.JNIEnv, cls C.jclass, method C.jmethodID, args ...jvalue) (C.jobject, error) { - res := C.gio_jni_CallStaticObjectMethodA(env, cls, method, varArgs(args)) - return res, exception(env) -} - -func callVoidMethod(env *C.JNIEnv, obj C.jobject, method C.jmethodID, args ...jvalue) error { - C.gio_jni_CallVoidMethodA(env, obj, method, varArgs(args)) - return exception(env) -} - -func callObjectMethod(env *C.JNIEnv, obj C.jobject, method C.jmethodID, args ...jvalue) (C.jobject, error) { - res := C.gio_jni_CallObjectMethodA(env, obj, method, varArgs(args)) - return res, exception(env) -} - -// exception returns an error corresponding to the pending -// exception, or nil if no exception is pending. The pending -// exception is cleared. -func exception(env *C.JNIEnv) error { - thr := C.gio_jni_ExceptionOccurred(env) - if thr == 0 { - return nil - } - C.gio_jni_ExceptionClear(env) - cls := getObjectClass(env, C.jobject(thr)) - toString := getMethodID(env, cls, "toString", "()Ljava/lang/String;") - msg, err := callObjectMethod(env, C.jobject(thr), toString) - if err != nil { - return err - } - return errors.New(goString(env, C.jstring(msg))) -} - -func getObjectClass(env *C.JNIEnv, obj C.jobject) C.jclass { - if obj == 0 { - panic("null object") - } - cls := C.gio_jni_GetObjectClass(env, C.jobject(obj)) - if err := exception(env); err != nil { - // GetObjectClass should never fail. - panic(err) - } - return cls -} - -// goString converts the JVM jstring to a Go string. -func goString(env *C.JNIEnv, str C.jstring) string { - if str == 0 { - return "" - } - strlen := C.gio_jni_GetStringLength(env, C.jstring(str)) - chars := C.gio_jni_GetStringChars(env, C.jstring(str)) - var utf16Chars []uint16 - hdr := (*reflect.SliceHeader)(unsafe.Pointer(&utf16Chars)) - hdr.Data = uintptr(unsafe.Pointer(chars)) - hdr.Cap = int(strlen) - hdr.Len = int(strlen) - utf8 := utf16.Decode(utf16Chars) - return string(utf8) -} - -func Main() { -} - -func NewWindow(window Callbacks, opts *Options) error { - mainWindow.in <- windowAndOptions{window, opts} - return <-mainWindow.errs -} - -func (w *window) WriteClipboard(s string) { - runOnMain(func(env *C.JNIEnv) { - jstr := javaString(env, s) - callStaticVoidMethod(env, android.gioCls, android.mwriteClipboard, - jvalue(android.appCtx), jvalue(jstr)) - }) -} - -func (w *window) ReadClipboard() { - runOnMain(func(env *C.JNIEnv) { - c, err := callStaticObjectMethod(env, android.gioCls, android.mreadClipboard, - jvalue(android.appCtx)) - if err != nil { - return - } - content := goString(env, C.jstring(c)) - w.callbacks.Event(system.ClipboardEvent{Text: content}) - }) -} - -// Close the window. Not implemented for Android. -func (w *window) Close() {} - -// RunOnMain is the exported version of runOnMain without a JNI -// environement. -func RunOnMain(f func()) { - runOnMain(func(_ *C.JNIEnv) { - f() - }) -} - -// runOnMain runs a function on the Java main thread. -func runOnMain(f func(env *C.JNIEnv)) { - go func() { - mainFuncs <- f - runInJVM(javaVM(), func(env *C.JNIEnv) { - callStaticVoidMethod(env, android.gioCls, android.mwakeupMainThread) - }) - }() -} - -//export Java_org_gioui_Gio_scheduleMainFuncs -func Java_org_gioui_Gio_scheduleMainFuncs(env *C.JNIEnv, cls C.jclass) { - for { - select { - case f := <-mainFuncs: - f(env) - default: - return - } - } -} diff --git a/vendor/gioui.org/app/internal/window/os_darwin.m b/vendor/gioui.org/app/internal/window/os_darwin.m deleted file mode 100644 index dc4f00b..0000000 --- a/vendor/gioui.org/app/internal/window/os_darwin.m +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -@import Dispatch; -@import Foundation; - -#include "_cgo_export.h" - -void gio_wakeupMainThread(void) { - dispatch_async(dispatch_get_main_queue(), ^{ - gio_dispatchMainFuncs(); - }); -} - -NSUInteger gio_nsstringLength(CFTypeRef cstr) { - NSString *str = (__bridge NSString *)cstr; - return [str length]; -} - -void gio_nsstringGetCharacters(CFTypeRef cstr, unichar *chars, NSUInteger loc, NSUInteger length) { - NSString *str = (__bridge NSString *)cstr; - [str getCharacters:chars range:NSMakeRange(loc, length)]; -} diff --git a/vendor/gioui.org/app/internal/window/os_macos.go b/vendor/gioui.org/app/internal/window/os_macos.go deleted file mode 100644 index ef95b70..0000000 --- a/vendor/gioui.org/app/internal/window/os_macos.go +++ /dev/null @@ -1,460 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -// +build darwin,!ios - -package window - -import ( - "errors" - "image" - "runtime" - "time" - "unicode" - "unicode/utf16" - "unsafe" - - "gioui.org/f32" - "gioui.org/io/key" - "gioui.org/io/pointer" - "gioui.org/io/system" - "gioui.org/unit" - - _ "gioui.org/app/internal/cocoainit" -) - -/* -#cgo CFLAGS: -DGL_SILENCE_DEPRECATION -Werror -Wno-deprecated-declarations -fmodules -fobjc-arc -x objective-c - -#include <AppKit/AppKit.h> - -#define GIO_MOUSE_MOVE 1 -#define GIO_MOUSE_UP 2 -#define GIO_MOUSE_DOWN 3 -#define GIO_MOUSE_SCROLL 4 - -__attribute__ ((visibility ("hidden"))) void gio_main(void); -__attribute__ ((visibility ("hidden"))) CGFloat gio_viewWidth(CFTypeRef viewRef); -__attribute__ ((visibility ("hidden"))) CGFloat gio_viewHeight(CFTypeRef viewRef); -__attribute__ ((visibility ("hidden"))) CGFloat gio_getViewBackingScale(CFTypeRef viewRef); -__attribute__ ((visibility ("hidden"))) CGFloat gio_getScreenBackingScale(void); -__attribute__ ((visibility ("hidden"))) CFTypeRef gio_readClipboard(void); -__attribute__ ((visibility ("hidden"))) void gio_writeClipboard(unichar *chars, NSUInteger length); -__attribute__ ((visibility ("hidden"))) void gio_setNeedsDisplay(CFTypeRef viewRef); -__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight); -__attribute__ ((visibility ("hidden"))) void gio_makeKeyAndOrderFront(CFTypeRef windowRef); -__attribute__ ((visibility ("hidden"))) NSPoint gio_cascadeTopLeftFromPoint(CFTypeRef windowRef, NSPoint topLeft); -__attribute__ ((visibility ("hidden"))) void gio_close(CFTypeRef windowRef); -*/ -import "C" - -func init() { - // Darwin requires that UI operations happen on the main thread only. - runtime.LockOSThread() -} - -type window struct { - view C.CFTypeRef - window C.CFTypeRef - w Callbacks - stage system.Stage - displayLink *displayLink - - scale float32 -} - -// viewMap is the mapping from Cocoa NSViews to Go windows. -var viewMap = make(map[C.CFTypeRef]*window) - -var viewFactory func() C.CFTypeRef - -// launched is closed when applicationDidFinishLaunching is called. -var launched = make(chan struct{}) - -// nextTopLeft is the offset to use for the next window's call to -// cascadeTopLeftFromPoint. -var nextTopLeft C.NSPoint - -// mustView is like lookoupView, except that it panics -// if the view isn't mapped. -func mustView(view C.CFTypeRef) *window { - w, ok := lookupView(view) - if !ok { - panic("no window for view") - } - return w -} - -func lookupView(view C.CFTypeRef) (*window, bool) { - w, exists := viewMap[view] - if !exists { - return nil, false - } - return w, true -} - -func deleteView(view C.CFTypeRef) { - delete(viewMap, view) -} - -func insertView(view C.CFTypeRef, w *window) { - viewMap[view] = w -} - -func (w *window) contextView() C.CFTypeRef { - return w.view -} - -func (w *window) ReadClipboard() { - runOnMain(func() { - content := nsstringToString(C.gio_readClipboard()) - w.w.Event(system.ClipboardEvent{Text: content}) - }) -} - -func (w *window) WriteClipboard(s string) { - u16 := utf16.Encode([]rune(s)) - runOnMain(func() { - var chars *C.unichar - if len(u16) > 0 { - chars = (*C.unichar)(unsafe.Pointer(&u16[0])) - } - C.gio_writeClipboard(chars, C.NSUInteger(len(u16))) - }) -} - -func (w *window) ShowTextInput(show bool) {} - -func (w *window) SetAnimating(anim bool) { - if anim { - w.displayLink.Start() - } else { - w.displayLink.Stop() - } -} - -func (w *window) Close() { - runOnMain(func() { - // Make sure the view is still valid. The window might've been closed - // during the switch to the main thread. - if w.view != 0 { - C.gio_close(w.window) - } - }) -} - -func (w *window) setStage(stage system.Stage) { - if stage == w.stage { - return - } - w.stage = stage - w.w.Event(system.StageEvent{Stage: stage}) -} - -//export gio_onKeys -func gio_onKeys(view C.CFTypeRef, cstr *C.char, ti C.double, mods C.NSUInteger) { - str := C.GoString(cstr) - kmods := convertMods(mods) - w := mustView(view) - for _, k := range str { - if n, ok := convertKey(k); ok { - w.w.Event(key.Event{ - Name: n, - Modifiers: kmods, - }) - } - } -} - -//export gio_onText -func gio_onText(view C.CFTypeRef, cstr *C.char) { - str := C.GoString(cstr) - w := mustView(view) - w.w.Event(key.EditEvent{Text: str}) -} - -//export gio_onMouse -func gio_onMouse(view C.CFTypeRef, cdir C.int, cbtns C.NSUInteger, x, y, dx, dy C.CGFloat, ti C.double, mods C.NSUInteger) { - var typ pointer.Type - switch cdir { - case C.GIO_MOUSE_MOVE: - typ = pointer.Move - case C.GIO_MOUSE_UP: - typ = pointer.Release - case C.GIO_MOUSE_DOWN: - typ = pointer.Press - case C.GIO_MOUSE_SCROLL: - typ = pointer.Scroll - default: - panic("invalid direction") - } - var btns pointer.Buttons - if cbtns&(1<<0) != 0 { - btns |= pointer.ButtonLeft - } - if cbtns&(1<<1) != 0 { - btns |= pointer.ButtonRight - } - if cbtns&(1<<2) != 0 { - btns |= pointer.ButtonMiddle - } - t := time.Duration(float64(ti)*float64(time.Second) + .5) - w := mustView(view) - xf, yf := float32(x)*w.scale, float32(y)*w.scale - dxf, dyf := float32(dx)*w.scale, float32(dy)*w.scale - w.w.Event(pointer.Event{ - Type: typ, - Source: pointer.Mouse, - Time: t, - Buttons: btns, - Position: f32.Point{X: xf, Y: yf}, - Scroll: f32.Point{X: dxf, Y: dyf}, - Modifiers: convertMods(mods), - }) -} - -//export gio_onDraw -func gio_onDraw(view C.CFTypeRef) { - w := mustView(view) - w.draw() -} - -//export gio_onFocus -func gio_onFocus(view C.CFTypeRef, focus C.BOOL) { - w := mustView(view) - w.w.Event(key.FocusEvent{Focus: focus == C.YES}) -} - -//export gio_onChangeScreen -func gio_onChangeScreen(view C.CFTypeRef, did uint64) { - w := mustView(view) - w.displayLink.SetDisplayID(did) -} - -func (w *window) draw() { - w.scale = float32(C.gio_getViewBackingScale(w.view)) - wf, hf := float32(C.gio_viewWidth(w.view)), float32(C.gio_viewHeight(w.view)) - if wf == 0 || hf == 0 { - return - } - width := int(wf*w.scale + .5) - height := int(hf*w.scale + .5) - cfg := configFor(w.scale) - w.setStage(system.StageRunning) - w.w.Event(FrameEvent{ - FrameEvent: system.FrameEvent{ - Now: time.Now(), - Size: image.Point{ - X: width, - Y: height, - }, - Metric: cfg, - }, - Sync: true, - }) -} - -func configFor(scale float32) unit.Metric { - return unit.Metric{ - PxPerDp: scale, - PxPerSp: scale, - } -} - -//export gio_onClose -func gio_onClose(view C.CFTypeRef) { - w := mustView(view) - w.displayLink.Close() - deleteView(view) - w.w.Event(system.DestroyEvent{}) - C.CFRelease(w.view) - w.view = 0 - C.CFRelease(w.window) - w.window = 0 -} - -//export gio_onHide -func gio_onHide(view C.CFTypeRef) { - w := mustView(view) - w.setStage(system.StagePaused) -} - -//export gio_onShow -func gio_onShow(view C.CFTypeRef) { - w := mustView(view) - w.setStage(system.StageRunning) -} - -//export gio_onAppHide -func gio_onAppHide() { - for _, w := range viewMap { - w.setStage(system.StagePaused) - } -} - -//export gio_onAppShow -func gio_onAppShow() { - for _, w := range viewMap { - w.setStage(system.StageRunning) - } -} - -//export gio_onFinishLaunching -func gio_onFinishLaunching() { - close(launched) -} - -func NewWindow(win Callbacks, opts *Options) error { - <-launched - errch := make(chan error) - runOnMain(func() { - w, err := newWindow(opts) - if err != nil { - errch <- err - return - } - screenScale := float32(C.gio_getScreenBackingScale()) - cfg := configFor(screenScale) - width := cfg.Px(opts.Width) - height := cfg.Px(opts.Height) - // Window sizes is in unscaled screen coordinates, not device pixels. - width = int(float32(width) / screenScale) - height = int(float32(height) / screenScale) - minWidth := cfg.Px(opts.MinWidth) - minHeight := cfg.Px(opts.MinHeight) - minWidth = int(float32(minWidth) / screenScale) - minHeight = int(float32(minHeight) / screenScale) - maxWidth := cfg.Px(opts.MaxWidth) - maxHeight := cfg.Px(opts.MaxHeight) - maxWidth = int(float32(maxWidth) / screenScale) - maxHeight = int(float32(maxHeight) / screenScale) - title := C.CString(opts.Title) - defer C.free(unsafe.Pointer(title)) - errch <- nil - win.SetDriver(w) - w.w = win - w.window = C.gio_createWindow(w.view, title, C.CGFloat(width), C.CGFloat(height), - C.CGFloat(minWidth), C.CGFloat(minHeight), C.CGFloat(maxWidth), C.CGFloat(maxHeight)) - if nextTopLeft.x == 0 && nextTopLeft.y == 0 { - // cascadeTopLeftFromPoint treats (0, 0) as a no-op, - // and just returns the offset we need for the first window. - nextTopLeft = C.gio_cascadeTopLeftFromPoint(w.window, nextTopLeft) - } - nextTopLeft = C.gio_cascadeTopLeftFromPoint(w.window, nextTopLeft) - C.gio_makeKeyAndOrderFront(w.window) - }) - return <-errch -} - -func newWindow(opts *Options) (*window, error) { - view := viewFactory() - if view == 0 { - return nil, errors.New("CreateWindow: failed to create view") - } - scale := float32(C.gio_getViewBackingScale(view)) - w := &window{ - view: view, - scale: scale, - } - dl, err := NewDisplayLink(func() { - runOnMain(func() { - if w.view != 0 { - C.gio_setNeedsDisplay(w.view) - } - }) - }) - w.displayLink = dl - if err != nil { - C.CFRelease(view) - return nil, err - } - insertView(view, w) - return w, nil -} - -func Main() { - C.gio_main() -} - -func convertKey(k rune) (string, bool) { - var n string - switch k { - case 0x1b: - n = key.NameEscape - case C.NSLeftArrowFunctionKey: - n = key.NameLeftArrow - case C.NSRightArrowFunctionKey: - n = key.NameRightArrow - case C.NSUpArrowFunctionKey: - n = key.NameUpArrow - case C.NSDownArrowFunctionKey: - n = key.NameDownArrow - case 0xd: - n = key.NameReturn - case 0x3: - n = key.NameEnter - case C.NSHomeFunctionKey: - n = key.NameHome - case C.NSEndFunctionKey: - n = key.NameEnd - case 0x7f: - n = key.NameDeleteBackward - case C.NSDeleteFunctionKey: - n = key.NameDeleteForward - case C.NSPageUpFunctionKey: - n = key.NamePageUp - case C.NSPageDownFunctionKey: - n = key.NamePageDown - case C.NSF1FunctionKey: - n = "F1" - case C.NSF2FunctionKey: - n = "F2" - case C.NSF3FunctionKey: - n = "F3" - case C.NSF4FunctionKey: - n = "F4" - case C.NSF5FunctionKey: - n = "F5" - case C.NSF6FunctionKey: - n = "F6" - case C.NSF7FunctionKey: - n = "F7" - case C.NSF8FunctionKey: - n = "F8" - case C.NSF9FunctionKey: - n = "F9" - case C.NSF10FunctionKey: - n = "F10" - case C.NSF11FunctionKey: - n = "F11" - case C.NSF12FunctionKey: - n = "F12" - case 0x09, 0x19: - n = key.NameTab - case 0x20: - n = "Space" - default: - k = unicode.ToUpper(k) - if !unicode.IsPrint(k) { - return "", false - } - n = string(k) - } - return n, true -} - -func convertMods(mods C.NSUInteger) key.Modifiers { - var kmods key.Modifiers - if mods&C.NSAlternateKeyMask != 0 { - kmods |= key.ModAlt - } - if mods&C.NSControlKeyMask != 0 { - kmods |= key.ModCtrl - } - if mods&C.NSCommandKeyMask != 0 { - kmods |= key.ModCommand - } - if mods&C.NSShiftKeyMask != 0 { - kmods |= key.ModShift - } - return kmods -} diff --git a/vendor/gioui.org/app/internal/window/os_unix.go b/vendor/gioui.org/app/internal/window/os_unix.go deleted file mode 100644 index baaebb5..0000000 --- a/vendor/gioui.org/app/internal/window/os_unix.go +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -// +build linux,!android freebsd openbsd - -package window - -import ( - "errors" -) - -func Main() { - select {} -} - -// instead of creating files with build tags for each combination of wayland +/- x11 -// let each driver initialize these variables with their own version of createWindow. -var wlDriver, x11Driver func(Callbacks, *Options) error - -func NewWindow(window Callbacks, opts *Options) error { - var errFirst, err error - if wlDriver != nil { - if err = wlDriver(window, opts); err == nil { - return nil - } - errFirst = err - } - if x11Driver != nil { - if err = x11Driver(window, opts); err == nil { - return nil - } - if errFirst == nil { - errFirst = err - } - } - if errFirst != nil { - return errFirst - } - return errors.New("app: no window driver available") -} diff --git a/vendor/gioui.org/app/internal/window/window.go b/vendor/gioui.org/app/internal/window/window.go deleted file mode 100644 index 30ba360..0000000 --- a/vendor/gioui.org/app/internal/window/window.go +++ /dev/null @@ -1,102 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -// Package window implements platform specific windows -// and GPU contexts. -package window - -import ( - "errors" - - "gioui.org/gpu/backend" - "gioui.org/io/event" - "gioui.org/io/system" - "gioui.org/unit" -) - -type Options struct { - Width, Height unit.Value - MinWidth, MinHeight unit.Value - MaxWidth, MaxHeight unit.Value - Title string -} - -type FrameEvent struct { - system.FrameEvent - - Sync bool -} - -type Callbacks interface { - SetDriver(d Driver) - Event(e event.Event) -} - -type Context interface { - Backend() (backend.Device, error) - Present() error - MakeCurrent() error - Release() - Lock() - Unlock() -} - -// ErrDeviceLost is returned from Context.Present when -// the underlying GPU device is gone and should be -// recreated. -var ErrDeviceLost = errors.New("GPU device lost") - -// Driver is the interface for the platform implementation -// of a window. -type Driver interface { - // SetAnimating sets the animation flag. When the window is animating, - // FrameEvents are delivered as fast as the display can handle them. - SetAnimating(anim bool) - // ShowTextInput updates the virtual keyboard state. - ShowTextInput(show bool) - NewContext() (Context, error) - - // ReadClipboard requests the clipboard content. - ReadClipboard() - // WriteClipboard requests a clipboard write. - WriteClipboard(s string) - - // Close the window. - Close() -} - -type windowRendezvous struct { - in chan windowAndOptions - out chan windowAndOptions - errs chan error -} - -type windowAndOptions struct { - window Callbacks - opts *Options -} - -func newWindowRendezvous() *windowRendezvous { - wr := &windowRendezvous{ - in: make(chan windowAndOptions), - out: make(chan windowAndOptions), - errs: make(chan error), - } - go func() { - var main windowAndOptions - var out chan windowAndOptions - for { - select { - case w := <-wr.in: - var err error - if main.window != nil { - err = errors.New("multiple windows are not supported") - } - wr.errs <- err - main = w - out = wr.out - case out <- main: - } - } - }() - return wr -} diff --git a/vendor/gioui.org/app/internal/windows/windows.go b/vendor/gioui.org/app/internal/windows/windows.go index 9006326..65a01d3 100644 --- a/vendor/gioui.org/app/internal/windows/windows.go +++ b/vendor/gioui.org/app/internal/windows/windows.go @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Unlicense OR MIT +//go:build windows // +build windows package windows @@ -54,14 +55,44 @@ type MinMaxInfo struct { PtMaxTrackSize Point } +type WindowPlacement struct { + length uint32 + flags uint32 + showCmd uint32 + ptMinPosition Point + ptMaxPosition Point + rcNormalPosition Rect + rcDevice Rect +} + +type MonitorInfo struct { + cbSize uint32 + Monitor Rect + WorkArea Rect + Flags uint32 +} + const ( + TRUE = 1 + CS_HREDRAW = 0x0002 CS_VREDRAW = 0x0001 CS_OWNDC = 0x0020 CW_USEDEFAULT = -2147483648 - IDC_ARROW = 32512 + GWL_STYLE = ^(uint32(16) - 1) // -16 + HWND_TOPMOST = ^(uint32(1) - 1) // -1 + + HTCLIENT = 1 + + IDC_ARROW = 32512 + IDC_IBEAM = 32513 + IDC_HAND = 32649 + IDC_CROSS = 32515 + IDC_SIZENS = 32645 + IDC_SIZEWE = 32644 + IDC_SIZEALL = 32646 INFINITE = 0xFFFFFFFF @@ -77,6 +108,13 @@ const ( SW_SHOWDEFAULT = 10 + SWP_FRAMECHANGED = 0x0020 + SWP_NOMOVE = 0x0002 + SWP_NOOWNERZORDER = 0x0200 + SWP_NOSIZE = 0x0001 + SWP_NOZORDER = 0x0004 + SWP_SHOWWINDOW = 0x0040 + USER_TIMER_MINIMUM = 0x0000000A VK_CONTROL = 0x11 @@ -142,14 +180,17 @@ const ( WM_MBUTTONUP = 0x0208 WM_MOUSEMOVE = 0x0200 WM_MOUSEWHEEL = 0x020A + WM_MOUSEHWHEEL = 0x020E WM_PAINT = 0x000F WM_CLOSE = 0x0010 WM_QUIT = 0x0012 + WM_SETCURSOR = 0x0020 WM_SETFOCUS = 0x0007 WM_KILLFOCUS = 0x0008 WM_SHOWWINDOW = 0x0018 WM_SIZE = 0x0005 WM_SYSKEYDOWN = 0x0104 + WM_SYSKEYUP = 0x0105 WM_RBUTTONDOWN = 0x0204 WM_RBUTTONUP = 0x0205 WM_TIMER = 0x0113 @@ -159,6 +200,7 @@ const ( WS_CLIPCHILDREN = 0x00010000 WS_CLIPSIBLINGS = 0x04000000 + WS_MAXIMIZE = 0x01000000 WS_VISIBLE = 0x10000000 WS_OVERLAPPED = 0x00000000 WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | @@ -185,6 +227,19 @@ const ( GHND = 0x0042 CF_UNICODETEXT = 13 + IMAGE_BITMAP = 0 + IMAGE_ICON = 1 + IMAGE_CURSOR = 2 + + LR_CREATEDIBSECTION = 0x00002000 + LR_DEFAULTCOLOR = 0x00000000 + LR_DEFAULTSIZE = 0x00000040 + LR_LOADFROMFILE = 0x00000010 + LR_LOADMAP3DCOLORS = 0x00001000 + LR_LOADTRANSPARENT = 0x00000020 + LR_MONOCHROME = 0x00000001 + LR_SHARED = 0x00008000 + LR_VGACOLOR = 0x00000080 ) var ( @@ -207,12 +262,20 @@ var ( _GetClientRect = user32.NewProc("GetClientRect") _GetClipboardData = user32.NewProc("GetClipboardData") _GetDC = user32.NewProc("GetDC") + _GetDpiForWindow = user32.NewProc("GetDpiForWindow") _GetKeyState = user32.NewProc("GetKeyState") _GetMessage = user32.NewProc("GetMessageW") _GetMessageTime = user32.NewProc("GetMessageTime") + _GetMonitorInfo = user32.NewProc("GetMonitorInfoW") + _GetWindowLong = user32.NewProc("GetWindowLongPtrW") + _GetWindowLong32 = user32.NewProc("GetWindowLongW") + _GetWindowPlacement = user32.NewProc("GetWindowPlacement") _KillTimer = user32.NewProc("KillTimer") _LoadCursor = user32.NewProc("LoadCursorW") + _LoadImage = user32.NewProc("LoadImageW") _MonitorFromPoint = user32.NewProc("MonitorFromPoint") + _MonitorFromWindow = user32.NewProc("MonitorFromWindow") + _MoveWindow = user32.NewProc("MoveWindow") _MsgWaitForMultipleObjectsEx = user32.NewProc("MsgWaitForMultipleObjectsEx") _OpenClipboard = user32.NewProc("OpenClipboard") _PeekMessage = user32.NewProc("PeekMessageW") @@ -224,11 +287,17 @@ var ( _ScreenToClient = user32.NewProc("ScreenToClient") _ShowWindow = user32.NewProc("ShowWindow") _SetCapture = user32.NewProc("SetCapture") + _SetCursor = user32.NewProc("SetCursor") _SetClipboardData = user32.NewProc("SetClipboardData") _SetForegroundWindow = user32.NewProc("SetForegroundWindow") _SetFocus = user32.NewProc("SetFocus") _SetProcessDPIAware = user32.NewProc("SetProcessDPIAware") _SetTimer = user32.NewProc("SetTimer") + _SetWindowLong = user32.NewProc("SetWindowLongPtrW") + _SetWindowLong32 = user32.NewProc("SetWindowLongW") + _SetWindowPlacement = user32.NewProc("SetWindowPlacement") + _SetWindowPos = user32.NewProc("SetWindowPos") + _SetWindowText = user32.NewProc("SetWindowTextW") _TranslateMessage = user32.NewProc("TranslateMessage") _UnregisterClass = user32.NewProc("UnregisterClassW") _UpdateWindow = user32.NewProc("UpdateWindow") @@ -343,7 +412,7 @@ func getDpiForMonitor(hmonitor syscall.Handle, dpiType uint32) int { // GetSystemDPI returns the effective DPI of the system. func GetSystemDPI() int { - // Check for getDpiForMonitor, introduced in Windows 8.1. + // Check for GetDpiForMonitor, introduced in Windows 8.1. if _GetDpiForMonitor.Find() == nil { hmon := monitorFromPoint(Point{}, MONITOR_DEFAULTTOPRIMARY) return getDpiForMonitor(hmon, MDT_EFFECTIVE_DPI) @@ -377,6 +446,66 @@ func GetMessageTime() time.Duration { return time.Duration(r) * time.Millisecond } +// GetWindowDPI returns the effective DPI of the window. +func GetWindowDPI(hwnd syscall.Handle) int { + // Check for GetDpiForWindow, introduced in Windows 10. + if _GetDpiForWindow.Find() == nil { + dpi, _, _ := _GetDpiForWindow.Call(uintptr(hwnd)) + return int(dpi) + } else { + return GetSystemDPI() + } +} + +func GetWindowPlacement(hwnd syscall.Handle) *WindowPlacement { + var wp WindowPlacement + wp.length = uint32(unsafe.Sizeof(wp)) + _GetWindowPlacement.Call(uintptr(hwnd), uintptr(unsafe.Pointer(&wp))) + return &wp +} + +func GetMonitorInfo(hwnd syscall.Handle) MonitorInfo { + var mi MonitorInfo + mi.cbSize = uint32(unsafe.Sizeof(mi)) + v, _, _ := _MonitorFromWindow.Call(uintptr(hwnd), MONITOR_DEFAULTTOPRIMARY) + _GetMonitorInfo.Call(v, uintptr(unsafe.Pointer(&mi))) + return mi +} + +func GetWindowLong(hwnd syscall.Handle) (style uintptr) { + if runtime.GOARCH == "386" { + style, _, _ = _GetWindowLong32.Call(uintptr(hwnd), uintptr(GWL_STYLE)) + } else { + style, _, _ = _GetWindowLong.Call(uintptr(hwnd), uintptr(GWL_STYLE)) + } + return +} + +func SetWindowLong(hwnd syscall.Handle, idx uint32, style uintptr) { + if runtime.GOARCH == "386" { + _SetWindowLong32.Call(uintptr(hwnd), uintptr(idx), style) + } else { + _SetWindowLong.Call(uintptr(hwnd), uintptr(idx), style) + } +} + +func SetWindowPlacement(hwnd syscall.Handle, wp *WindowPlacement) { + _SetWindowPlacement.Call(uintptr(hwnd), uintptr(unsafe.Pointer(wp))) +} + +func SetWindowPos(hwnd syscall.Handle, hwndInsertAfter uint32, x, y, dx, dy int32, style uintptr) { + _SetWindowPos.Call(uintptr(hwnd), uintptr(hwndInsertAfter), + uintptr(x), uintptr(y), + uintptr(dx), uintptr(dy), + style, + ) +} + +func SetWindowText(hwnd syscall.Handle, title string) { + wname := syscall.StringToUTF16Ptr(title) + _SetWindowText.Call(uintptr(hwnd), uintptr(unsafe.Pointer(wname))) +} + func GlobalAlloc(size int) (syscall.Handle, error) { r, _, err := _GlobalAlloc.Call(GHND, uintptr(size)) if r == 0 { @@ -417,6 +546,22 @@ func LoadCursor(curID uint16) (syscall.Handle, error) { return syscall.Handle(h), nil } +func LoadImage(hInst syscall.Handle, res uint32, typ uint32, cx, cy int, fuload uint32) (syscall.Handle, error) { + h, _, err := _LoadImage.Call(uintptr(hInst), uintptr(res), uintptr(typ), uintptr(cx), uintptr(cy), uintptr(fuload)) + if h == 0 { + return 0, fmt.Errorf("LoadImageW failed: %v", err) + } + return syscall.Handle(h), nil +} + +func MoveWindow(hwnd syscall.Handle, x, y, width, height int32, repaint bool) { + var paint uintptr + if repaint { + paint = TRUE + } + _MoveWindow.Call(uintptr(hwnd), uintptr(x), uintptr(y), uintptr(width), uintptr(height), paint) +} + func monitorFromPoint(pt Point, flags uint32) syscall.Handle { r, _, _ := _MonitorFromPoint.Call(uintptr(pt.X), uintptr(pt.Y), uintptr(flags)) return syscall.Handle(r) @@ -500,6 +645,10 @@ func SetClipboardData(format uint32, mem syscall.Handle) error { return nil } +func SetCursor(h syscall.Handle) { + _SetCursor.Call(uintptr(h)) +} + func SetTimer(hwnd syscall.Handle, nIDEvent uintptr, uElapse uint32, timerProc uintptr) error { r, _, err := _SetTimer.Call(uintptr(hwnd), uintptr(nIDEvent), uintptr(uElapse), timerProc) if r == 0 { @@ -530,6 +679,10 @@ func UpdateWindow(hwnd syscall.Handle) { _UpdateWindow.Call(uintptr(hwnd)) } +func (p WindowPlacement) Rect() Rect { + return p.rcNormalPosition +} + // issue34474KeepAlive calls runtime.KeepAlive as a // workaround for golang.org/issue/34474. func issue34474KeepAlive(v interface{}) { diff --git a/vendor/gioui.org/app/internal/xkb/xkb_unix.go b/vendor/gioui.org/app/internal/xkb/xkb_unix.go index 3525692..68b1329 100644 --- a/vendor/gioui.org/app/internal/xkb/xkb_unix.go +++ b/vendor/gioui.org/app/internal/xkb/xkb_unix.go @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Unlicense OR MIT +//go:build (linux && !android) || freebsd || openbsd // +build linux,!android freebsd openbsd // Package xkb implements a Go interface for the X Keyboard Extension library. @@ -150,7 +151,7 @@ func (x *Context) Modifiers() key.Modifiers { return mods } -func (x *Context) DispatchKey(keyCode uint32) (events []event.Event) { +func (x *Context) DispatchKey(keyCode uint32, state key.State) (events []event.Event) { if x.state == nil { return } @@ -163,6 +164,7 @@ func (x *Context) DispatchKey(keyCode uint32) (events []event.Event) { cmd := key.Event{ Name: name, Modifiers: x.Modifiers(), + State: state, } // Ensure that a physical backtab key is translated to // Shift-Tab. @@ -185,7 +187,10 @@ func (x *Context) DispatchKey(keyCode uint32) (events []event.Event) { C.xkb_compose_state_reset(x.compState) str = x.utf8Buf[:size] case C.XKB_COMPOSE_NOTHING: - str = x.charsForKeycode(kc) + mod := x.Modifiers() + if mod&(key.ModCtrl|key.ModAlt|key.ModSuper) == 0 { + str = x.charsForKeycode(kc) + } } // Report only printable runes. var n int @@ -198,7 +203,7 @@ func (x *Context) DispatchKey(keyCode uint32) (events []event.Event) { str = str[:len(str)-s] } } - if len(str) > 0 { + if state == key.Press && len(str) > 0 { events = append(events, key.EditEvent{Text: string(str)}) } return @@ -230,7 +235,7 @@ func convertKeysym(s C.xkb_keysym_t) (string, bool) { if 'a' <= s && s <= 'z' { return string(rune(s - 'a' + 'A')), true } - if ' ' <= s && s <= '~' { + if ' ' < s && s <= '~' { return string(rune(s)), true } var n string @@ -288,7 +293,7 @@ func convertKeysym(s C.xkb_keysym_t) (string, bool) { case C.XKB_KEY_Tab, C.XKB_KEY_KP_Tab, C.XKB_KEY_ISO_Left_Tab: n = key.NameTab case 0x20, C.XKB_KEY_KP_Space: - n = "Space" + n = key.NameSpace default: return "", false } diff --git a/vendor/gioui.org/app/loop.go b/vendor/gioui.org/app/loop.go deleted file mode 100644 index 0b2195a..0000000 --- a/vendor/gioui.org/app/loop.go +++ /dev/null @@ -1,157 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -package app - -import ( - "image" - "runtime" - - "gioui.org/app/internal/window" - "gioui.org/gpu" - "gioui.org/op" -) - -type renderLoop struct { - summary string - drawing bool - err error - - frames chan frame - results chan frameResult - refresh chan struct{} - refreshErr chan error - ack chan struct{} - stop chan struct{} - stopped chan struct{} -} - -type frame struct { - viewport image.Point - ops *op.Ops -} - -type frameResult struct { - profile string - err error -} - -func newLoop(ctx window.Context) (*renderLoop, error) { - l := &renderLoop{ - frames: make(chan frame), - results: make(chan frameResult), - refresh: make(chan struct{}), - refreshErr: make(chan error), - // Ack is buffered so GPU commands can be issued after - // ack'ing the frame. - ack: make(chan struct{}, 1), - stop: make(chan struct{}), - stopped: make(chan struct{}), - } - if err := l.renderLoop(ctx); err != nil { - return nil, err - } - return l, nil -} - -func (l *renderLoop) renderLoop(ctx window.Context) error { - // GL Operations must happen on a single OS thread, so - // pass initialization result through a channel. - initErr := make(chan error) - go func() { - defer close(l.stopped) - runtime.LockOSThread() - // Don't UnlockOSThread to avoid reuse by the Go runtime. - - if err := ctx.MakeCurrent(); err != nil { - initErr <- err - return - } - b, err := ctx.Backend() - if err != nil { - initErr <- err - return - } - g, err := gpu.New(b) - if err != nil { - initErr <- err - return - } - defer ctx.Release() - initErr <- nil - loop: - for { - select { - case <-l.refresh: - l.refreshErr <- ctx.MakeCurrent() - case frame := <-l.frames: - ctx.Lock() - g.Collect(frame.viewport, frame.ops) - // Signal that we're done with the frame ops. - l.ack <- struct{}{} - g.BeginFrame() - var res frameResult - res.err = ctx.Present() - g.EndFrame() - res.profile = g.Profile() - ctx.Unlock() - l.results <- res - case <-l.stop: - break loop - } - } - }() - return <-initErr -} - -func (l *renderLoop) Release() { - // Flush error. - l.Flush() - close(l.stop) - <-l.stopped - l.stop = nil -} - -func (l *renderLoop) Flush() error { - if l.drawing { - st := <-l.results - l.setErr(st.err) - if st.profile != "" { - l.summary = st.profile - } - l.drawing = false - } - return l.err -} - -func (l *renderLoop) Summary() string { - return l.summary -} - -func (l *renderLoop) Refresh() { - if l.err != nil { - return - } - // Make sure any pending frame is complete. - l.Flush() - l.refresh <- struct{}{} - l.setErr(<-l.refreshErr) -} - -// Draw initiates a draw of a frame. It returns a channel -// than signals when the frame is no longer being accessed. -func (l *renderLoop) Draw(viewport image.Point, frameOps *op.Ops) <-chan struct{} { - if l.err != nil { - l.ack <- struct{}{} - return l.ack - } - l.Flush() - l.frames <- frame{viewport, frameOps} - l.drawing = true - return l.ack -} - -func (l *renderLoop) setErr(err error) { - if l.err == nil { - l.err = err - } -} diff --git a/vendor/gioui.org/app/metal_darwin.go b/vendor/gioui.org/app/metal_darwin.go new file mode 100644 index 0000000..06b89f3 --- /dev/null +++ b/vendor/gioui.org/app/metal_darwin.go @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build !nometal +// +build !nometal + +package app + +import ( + "errors" + + "gioui.org/gpu" +) + +/* +#cgo CFLAGS: -Werror -xobjective-c -fmodules -fobjc-arc + +@import Metal; +@import QuartzCore.CAMetalLayer; + +#include <CoreFoundation/CoreFoundation.h> + +static CFTypeRef createMetalDevice(void) { + @autoreleasepool { + id<MTLDevice> dev = MTLCreateSystemDefaultDevice(); + return CFBridgingRetain(dev); + } +} + +static void setupLayer(CFTypeRef layerRef, CFTypeRef devRef) { + @autoreleasepool { + CAMetalLayer *layer = (__bridge CAMetalLayer *)layerRef; + id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef; + layer.device = dev; + // Package gpu assumes an sRGB-encoded framebuffer. + layer.pixelFormat = MTLPixelFormatBGRA8Unorm_sRGB; + if (@available(iOS 11.0, *)) { + // Never let nextDrawable time out and return nil. + layer.allowsNextDrawableTimeout = NO; + } + } +} + +static CFTypeRef nextDrawable(CFTypeRef layerRef) { + @autoreleasepool { + CAMetalLayer *layer = (__bridge CAMetalLayer *)layerRef; + return CFBridgingRetain([layer nextDrawable]); + } +} + +static CFTypeRef drawableTexture(CFTypeRef drawableRef) { + @autoreleasepool { + id<CAMetalDrawable> drawable = (__bridge id<CAMetalDrawable>)drawableRef; + return CFBridgingRetain(drawable.texture); + } +} + +static void presentDrawable(CFTypeRef queueRef, CFTypeRef drawableRef) { + @autoreleasepool { + id<MTLDrawable> drawable = (__bridge id<MTLDrawable>)drawableRef; + id<MTLCommandQueue> queue = (__bridge id<MTLCommandQueue>)queueRef; + id<MTLCommandBuffer> cmdBuffer = [queue commandBuffer]; + [cmdBuffer presentDrawable:drawable]; + [cmdBuffer commit]; + } +} + +static CFTypeRef newCommandQueue(CFTypeRef devRef) { + @autoreleasepool { + id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef; + return CFBridgingRetain([dev newCommandQueue]); + } +} +*/ +import "C" + +type mtlContext struct { + dev C.CFTypeRef + view C.CFTypeRef + layer C.CFTypeRef + queue C.CFTypeRef + drawable C.CFTypeRef + texture C.CFTypeRef +} + +func newMtlContext(w *window) (*mtlContext, error) { + dev := C.createMetalDevice() + if dev == 0 { + return nil, errors.New("metal: MTLCreateSystemDefaultDevice failed") + } + view := w.contextView() + layer := getMetalLayer(view) + if layer == 0 { + C.CFRelease(dev) + return nil, errors.New("metal: CAMetalLayer construction failed") + } + queue := C.newCommandQueue(dev) + if layer == 0 { + C.CFRelease(dev) + C.CFRelease(layer) + return nil, errors.New("metal: [MTLDevice newCommandQueue] failed") + } + C.setupLayer(layer, dev) + c := &mtlContext{ + dev: dev, + view: view, + layer: layer, + queue: queue, + } + return c, nil +} + +func (c *mtlContext) RenderTarget() (gpu.RenderTarget, error) { + if c.drawable != 0 || c.texture != 0 { + return nil, errors.New("metal:a previous RenderTarget wasn't Presented") + } + c.drawable = C.nextDrawable(c.layer) + if c.drawable == 0 { + return nil, errors.New("metal: [CAMetalLayer nextDrawable] failed") + } + c.texture = C.drawableTexture(c.drawable) + if c.texture == 0 { + return nil, errors.New("metal: CADrawable.texture is nil") + } + return gpu.MetalRenderTarget{ + Texture: uintptr(c.texture), + }, nil +} + +func (c *mtlContext) API() gpu.API { + return gpu.Metal{ + Device: uintptr(c.dev), + Queue: uintptr(c.queue), + PixelFormat: int(C.MTLPixelFormatBGRA8Unorm_sRGB), + } +} + +func (c *mtlContext) Release() { + C.CFRelease(c.queue) + C.CFRelease(c.dev) + C.CFRelease(c.layer) + if c.drawable != 0 { + C.CFRelease(c.drawable) + } + if c.texture != 0 { + C.CFRelease(c.texture) + } + *c = mtlContext{} +} + +func (c *mtlContext) Present() error { + C.CFRelease(c.texture) + c.texture = 0 + C.presentDrawable(c.queue, c.drawable) + C.CFRelease(c.drawable) + c.drawable = 0 + return nil +} + +func (c *mtlContext) Lock() error { + return nil +} + +func (c *mtlContext) Unlock() {} + +func (c *mtlContext) Refresh() error { + resizeDrawable(c.view, c.layer) + return nil +} + +func (w *window) NewContext() (context, error) { + return newMtlContext(w) +} diff --git a/vendor/gioui.org/app/metal_ios.go b/vendor/gioui.org/app/metal_ios.go new file mode 100644 index 0000000..860ba1a --- /dev/null +++ b/vendor/gioui.org/app/metal_ios.go @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build !nometal +// +build !nometal + +package app + +/* +#cgo CFLAGS: -Werror -xobjective-c -fmodules -fobjc-arc + +@import UIKit; + +@import QuartzCore.CAMetalLayer; + +#include <CoreFoundation/CoreFoundation.h> + +Class gio_layerClass(void) { + return [CAMetalLayer class]; +} + +static CFTypeRef getMetalLayer(CFTypeRef viewRef) { + @autoreleasepool { + UIView *view = (__bridge UIView *)viewRef; + return CFBridgingRetain(view.layer); + } +} + +static void resizeDrawable(CFTypeRef viewRef, CFTypeRef layerRef) { + @autoreleasepool { + UIView *view = (__bridge UIView *)viewRef; + CAMetalLayer *layer = (__bridge CAMetalLayer *)layerRef; + layer.contentsScale = view.contentScaleFactor; + CGSize size = layer.bounds.size; + size.width *= layer.contentsScale; + size.height *= layer.contentsScale; + layer.drawableSize = size; + } +} +*/ +import "C" + +func getMetalLayer(view C.CFTypeRef) C.CFTypeRef { + return C.getMetalLayer(view) +} + +func resizeDrawable(view, layer C.CFTypeRef) { + C.resizeDrawable(view, layer) +} diff --git a/vendor/gioui.org/app/metal_macos.go b/vendor/gioui.org/app/metal_macos.go new file mode 100644 index 0000000..7db06a9 --- /dev/null +++ b/vendor/gioui.org/app/metal_macos.go @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build darwin && !ios && !nometal +// +build darwin,!ios,!nometal + +package app + +/* +#cgo CFLAGS: -Werror -xobjective-c -fmodules -fobjc-arc + +@import AppKit; + +@import QuartzCore.CAMetalLayer; + +#include <CoreFoundation/CoreFoundation.h> + +CALayer *gio_layerFactory(void) { + @autoreleasepool { + return [CAMetalLayer layer]; + } +} + +static CFTypeRef getMetalLayer(CFTypeRef viewRef) { + @autoreleasepool { + NSView *view = (__bridge NSView *)viewRef; + return CFBridgingRetain(view.layer); + } +} + +static void resizeDrawable(CFTypeRef viewRef, CFTypeRef layerRef) { + @autoreleasepool { + NSView *view = (__bridge NSView *)viewRef; + CAMetalLayer *layer = (__bridge CAMetalLayer *)layerRef; + CGSize size = layer.bounds.size; + size.width *= layer.contentsScale; + size.height *= layer.contentsScale; + layer.drawableSize = size; + } +} +*/ +import "C" + +func getMetalLayer(view C.CFTypeRef) C.CFTypeRef { + return C.getMetalLayer(view) +} + +func resizeDrawable(view, layer C.CFTypeRef) { + C.resizeDrawable(view, layer) +} diff --git a/vendor/gioui.org/app/os.go b/vendor/gioui.org/app/os.go new file mode 100644 index 0000000..fc5c266 --- /dev/null +++ b/vendor/gioui.org/app/os.go @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +// package app implements platform specific windows +// and GPU contexts. +package app + +import ( + "errors" + "image" + "image/color" + + "gioui.org/io/key" + + "gioui.org/gpu" + "gioui.org/io/pointer" + "gioui.org/io/system" + "gioui.org/unit" +) + +type size struct { + Width unit.Value + Height unit.Value +} + +// errOutOfDate is reported when the GPU surface dimensions or properties no +// longer match the window. +var errOutOfDate = errors.New("app: GPU surface out of date") + +// Config describes a Window configuration. +type Config struct { + // Size is the window dimensions (Width, Height). + Size image.Point + // MaxSize is the window maximum allowed dimensions. + MaxSize image.Point + // MinSize is the window minimum allowed dimensions. + MinSize image.Point + // Title is the window title displayed in its decoration bar. + Title string + // WindowMode is the window mode. + Mode WindowMode + // StatusColor is the color of the Android status bar. + StatusColor color.NRGBA + // NavigationColor is the color of the navigation bar + // on Android, or the address bar in browsers. + NavigationColor color.NRGBA + // Orientation is the current window orientation. + Orientation Orientation + // CustomRenderer is true when the window content is rendered by the + // client. + CustomRenderer bool +} + +// ConfigEvent is sent whenever the configuration of a Window changes. +type ConfigEvent struct { + Config Config +} + +func (c *Config) apply(m unit.Metric, options []Option) { + for _, o := range options { + o(m, c) + } +} + +type wakeupEvent struct{} + +// WindowMode is the window mode (WindowMode.Option sets it). +// +// Supported platforms are macOS, X11, Windows, Android and JS. +type WindowMode uint8 + +const ( + // Windowed is the normal window mode with OS specific window decorations. + Windowed WindowMode = iota + // Fullscreen is the full screen window mode. + Fullscreen +) + +func (m WindowMode) Option() Option { + return func(_ unit.Metric, cnf *Config) { + cnf.Mode = m + } +} + +func (m WindowMode) String() string { + switch m { + case Windowed: + return "windowed" + case Fullscreen: + return "fullscreen" + } + return "" +} + +// Orientation is the orientation of the app (Orientation.Option sets it). +// +// Supported platforms are Android and JS. +type Orientation uint8 + +const ( + // AnyOrientation allows the window to be freely orientated. + AnyOrientation Orientation = iota + // LandscapeOrientation constrains the window to landscape orientations. + LandscapeOrientation + // PortraitOrientation constrains the window to portrait orientations. + PortraitOrientation +) + +func (o Orientation) Option() Option { + return func(_ unit.Metric, cnf *Config) { + cnf.Orientation = o + } +} + +func (o Orientation) String() string { + switch o { + case AnyOrientation: + return "any" + case LandscapeOrientation: + return "landscape" + case PortraitOrientation: + return "portrait" + } + return "" +} + +type frameEvent struct { + system.FrameEvent + + Sync bool +} + +type context interface { + API() gpu.API + RenderTarget() (gpu.RenderTarget, error) + Present() error + Refresh() error + Release() + Lock() error + Unlock() +} + +// Driver is the interface for the platform implementation +// of a window. +type driver interface { + // SetAnimating sets the animation flag. When the window is animating, + // FrameEvents are delivered as fast as the display can handle them. + SetAnimating(anim bool) + + // ShowTextInput updates the virtual keyboard state. + ShowTextInput(show bool) + + SetInputHint(mode key.InputHint) + + NewContext() (context, error) + + // ReadClipboard requests the clipboard content. + ReadClipboard() + // WriteClipboard requests a clipboard write. + WriteClipboard(s string) + + // Configure the window. + Configure([]Option) + + // SetCursor updates the current cursor to name. + SetCursor(name pointer.CursorName) + + // Raise the window at the top. + Raise() + + // Close the window. + Close() + + // Wakeup wakes up the event loop and sends a WakeupEvent. + Wakeup() + + // Maximize will make the window as large as possible, but keep the frame decorations. + Maximize() + // Center will place the window at monitor center. + Center() +} + +type windowRendezvous struct { + in chan windowAndConfig + out chan windowAndConfig + errs chan error +} + +type windowAndConfig struct { + window *callbacks + options []Option +} + +func newWindowRendezvous() *windowRendezvous { + wr := &windowRendezvous{ + in: make(chan windowAndConfig), + out: make(chan windowAndConfig), + errs: make(chan error), + } + go func() { + var main windowAndConfig + var out chan windowAndConfig + for { + select { + case w := <-wr.in: + var err error + if main.window != nil { + err = errors.New("multiple windows are not supported") + } + wr.errs <- err + main = w + out = wr.out + case out <- main: + } + } + }() + return wr +} + +func (wakeupEvent) ImplementsEvent() {} +func (ConfigEvent) ImplementsEvent() {} diff --git a/vendor/gioui.org/app/os_android.go b/vendor/gioui.org/app/os_android.go new file mode 100644 index 0000000..51d8289 --- /dev/null +++ b/vendor/gioui.org/app/os_android.go @@ -0,0 +1,1283 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +package app + +/* +#cgo CFLAGS: -Werror +#cgo LDFLAGS: -landroid + +#include <android/native_window_jni.h> +#include <android/configuration.h> +#include <android/keycodes.h> +#include <android/input.h> +#include <stdlib.h> + +static jint jni_GetEnv(JavaVM *vm, JNIEnv **env, jint version) { + return (*vm)->GetEnv(vm, (void **)env, version); +} + +static jint jni_GetJavaVM(JNIEnv *env, JavaVM **jvm) { + return (*env)->GetJavaVM(env, jvm); +} + +static jint jni_AttachCurrentThread(JavaVM *vm, JNIEnv **p_env, void *thr_args) { + return (*vm)->AttachCurrentThread(vm, p_env, thr_args); +} + +static jint jni_DetachCurrentThread(JavaVM *vm) { + return (*vm)->DetachCurrentThread(vm); +} + +static jobject jni_NewGlobalRef(JNIEnv *env, jobject obj) { + return (*env)->NewGlobalRef(env, obj); +} + +static void jni_DeleteGlobalRef(JNIEnv *env, jobject obj) { + (*env)->DeleteGlobalRef(env, obj); +} + +static jclass jni_GetObjectClass(JNIEnv *env, jobject obj) { + return (*env)->GetObjectClass(env, obj); +} + +static jmethodID jni_GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig) { + return (*env)->GetMethodID(env, clazz, name, sig); +} + +static jmethodID jni_GetStaticMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig) { + return (*env)->GetStaticMethodID(env, clazz, name, sig); +} + +static jfloat jni_CallFloatMethod(JNIEnv *env, jobject obj, jmethodID methodID) { + return (*env)->CallFloatMethod(env, obj, methodID); +} + +static jint jni_CallIntMethod(JNIEnv *env, jobject obj, jmethodID methodID) { + return (*env)->CallIntMethod(env, obj, methodID); +} + +static void jni_CallStaticVoidMethodA(JNIEnv *env, jclass cls, jmethodID methodID, const jvalue *args) { + (*env)->CallStaticVoidMethodA(env, cls, methodID, args); +} + +static void jni_CallVoidMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args) { + (*env)->CallVoidMethodA(env, obj, methodID, args); +} + +static jboolean jni_CallBooleanMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args) { + return (*env)->CallBooleanMethodA(env, obj, methodID, args); +} + +static jbyte *jni_GetByteArrayElements(JNIEnv *env, jbyteArray arr) { + return (*env)->GetByteArrayElements(env, arr, NULL); +} + +static void jni_ReleaseByteArrayElements(JNIEnv *env, jbyteArray arr, jbyte *bytes) { + (*env)->ReleaseByteArrayElements(env, arr, bytes, JNI_ABORT); +} + +static jsize jni_GetArrayLength(JNIEnv *env, jbyteArray arr) { + return (*env)->GetArrayLength(env, arr); +} + +static jstring jni_NewString(JNIEnv *env, const jchar *unicodeChars, jsize len) { + return (*env)->NewString(env, unicodeChars, len); +} + +static jsize jni_GetStringLength(JNIEnv *env, jstring str) { + return (*env)->GetStringLength(env, str); +} + +static const jchar *jni_GetStringChars(JNIEnv *env, jstring str) { + return (*env)->GetStringChars(env, str, NULL); +} + +static jthrowable jni_ExceptionOccurred(JNIEnv *env) { + return (*env)->ExceptionOccurred(env); +} + +static void jni_ExceptionClear(JNIEnv *env) { + (*env)->ExceptionClear(env); +} + +static jobject jni_CallObjectMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args) { + return (*env)->CallObjectMethodA(env, obj, method, args); +} + +static jobject jni_CallStaticObjectMethodA(JNIEnv *env, jclass cls, jmethodID method, jvalue *args) { + return (*env)->CallStaticObjectMethodA(env, cls, method, args); +} + +static jclass jni_FindClass(JNIEnv *env, char *name) { + return (*env)->FindClass(env, name); +} + +static jobject jni_NewObjectA(JNIEnv *env, jclass cls, jmethodID cons, jvalue *args) { + return (*env)->NewObjectA(env, cls, cons, args); +} +*/ +import "C" + +import ( + "errors" + "fmt" + "image" + "image/color" + "os" + "path/filepath" + "reflect" + "runtime" + "runtime/debug" + "sync" + "time" + "unicode/utf16" + "unsafe" + + "gioui.org/internal/f32color" + + "gioui.org/f32" + "gioui.org/io/clipboard" + "gioui.org/io/key" + "gioui.org/io/pointer" + "gioui.org/io/router" + "gioui.org/io/semantic" + "gioui.org/io/system" + "gioui.org/unit" +) + +type window struct { + callbacks *callbacks + + view C.jobject + + dpi int + fontScale float32 + insets system.Insets + + stage system.Stage + started bool + animating bool + + win *C.ANativeWindow + config Config + + semantic struct { + hoverID router.SemanticID + rootID router.SemanticID + focusID router.SemanticID + diffs []router.SemanticID + } +} + +// gioView hold cached JNI methods for GioView. +var gioView struct { + once sync.Once + getDensity C.jmethodID + getFontScale C.jmethodID + showTextInput C.jmethodID + hideTextInput C.jmethodID + setInputHint C.jmethodID + postInvalidate C.jmethodID // requests draw, called from non-UI thread + invalidate C.jmethodID // requests draw, called from UI thread + setCursor C.jmethodID + setOrientation C.jmethodID + setNavigationColor C.jmethodID + setStatusColor C.jmethodID + setFullscreen C.jmethodID + unregister C.jmethodID + sendA11yEvent C.jmethodID + sendA11yChange C.jmethodID + isA11yActive C.jmethodID +} + +// ViewEvent is sent whenever the Window's underlying Android view +// changes. +type ViewEvent struct { + // View is a JNI global reference to the android.view.View + // instance backing the Window. The reference is valid until + // the next ViewEvent is received. + // A zero View means that there is currently no view attached. + View uintptr +} + +type jvalue uint64 // The largest JNI type fits in 64 bits. + +var dataDirChan = make(chan string, 1) + +var android struct { + // mu protects all fields of this structure. However, once a + // non-nil jvm is returned from javaVM, all the other fields may + // be accessed unlocked. + mu sync.Mutex + jvm *C.JavaVM + + // appCtx is the global Android App context. + appCtx C.jobject + // gioCls is the class of the Gio class. + gioCls C.jclass + + mwriteClipboard C.jmethodID + mreadClipboard C.jmethodID + mwakeupMainThread C.jmethodID + + // android.view.accessibility.AccessibilityNodeInfo class. + accessibilityNodeInfo struct { + cls C.jclass + // addChild(View, int) + addChild C.jmethodID + // setBoundsInScreen(Rect) + setBoundsInScreen C.jmethodID + // setText(CharSequence) + setText C.jmethodID + // setContentDescription(CharSequence) + setContentDescription C.jmethodID + // setParent(View, int) + setParent C.jmethodID + // addAction(int) + addAction C.jmethodID + // setClassName(CharSequence) + setClassName C.jmethodID + // setCheckable(boolean) + setCheckable C.jmethodID + // setSelected(boolean) + setSelected C.jmethodID + // setChecked(boolean) + setChecked C.jmethodID + // setEnabled(boolean) + setEnabled C.jmethodID + // setAccessibilityFocused(boolean) + setAccessibilityFocused C.jmethodID + } + + // android.graphics.Rect class. + rect struct { + cls C.jclass + // (int, int, int, int) constructor. + cons C.jmethodID + } + + strings struct { + // "android.view.View" + androidViewView C.jstring + // "android.widget.Button" + androidWidgetButton C.jstring + // "android.widget.CheckBox" + androidWidgetCheckBox C.jstring + // "android.widget.EditText" + androidWidgetEditText C.jstring + // "android.widget.RadioButton" + androidWidgetRadioButton C.jstring + // "android.widget.Switch" + androidWidgetSwitch C.jstring + } +} + +// view maps from GioView JNI refenreces to windows. +var views = make(map[C.jlong]*window) + +var windows = make(map[*callbacks]*window) + +var mainWindow = newWindowRendezvous() + +var mainFuncs = make(chan func(env *C.JNIEnv), 1) + +var ( + dataDirOnce sync.Once + dataPath string +) + +var ( + newAndroidVulkanContext func(w *window) (context, error) + newAndroidGLESContext func(w *window) (context, error) +) + +// AccessibilityNodeProvider.HOST_VIEW_ID. +const HOST_VIEW_ID = -1 + +const ( + // AccessibilityEvent constants. + TYPE_VIEW_HOVER_ENTER = 128 + TYPE_VIEW_HOVER_EXIT = 256 +) + +const ( + // AccessibilityNodeInfo constants. + ACTION_ACCESSIBILITY_FOCUS = 64 + ACTION_CLEAR_ACCESSIBILITY_FOCUS = 128 + ACTION_CLICK = 16 +) + +func (w *window) NewContext() (context, error) { + funcs := []func(w *window) (context, error){newAndroidVulkanContext, newAndroidGLESContext} + var firstErr error + for _, f := range funcs { + if f == nil { + continue + } + c, err := f(w) + if err != nil { + if firstErr == nil { + firstErr = err + } + continue + } + return c, nil + } + if firstErr != nil { + return nil, firstErr + } + return nil, errors.New("x11: no available GPU backends") +} + +func dataDir() (string, error) { + dataDirOnce.Do(func() { + dataPath = <-dataDirChan + // Set XDG_CACHE_HOME to make os.UserCacheDir work. + if _, exists := os.LookupEnv("XDG_CACHE_HOME"); !exists { + cachePath := filepath.Join(dataPath, "cache") + os.Setenv("XDG_CACHE_HOME", cachePath) + } + // Set XDG_CONFIG_HOME to make os.UserConfigDir work. + if _, exists := os.LookupEnv("XDG_CONFIG_HOME"); !exists { + cfgPath := filepath.Join(dataPath, "config") + os.Setenv("XDG_CONFIG_HOME", cfgPath) + } + // Set HOME to make os.UserHomeDir work. + if _, exists := os.LookupEnv("HOME"); !exists { + os.Setenv("HOME", dataPath) + } + }) + return dataPath, nil +} + +func getMethodID(env *C.JNIEnv, class C.jclass, method, sig string) C.jmethodID { + m := C.CString(method) + defer C.free(unsafe.Pointer(m)) + s := C.CString(sig) + defer C.free(unsafe.Pointer(s)) + jm := C.jni_GetMethodID(env, class, m, s) + if err := exception(env); err != nil { + panic(err) + } + return jm +} + +func getStaticMethodID(env *C.JNIEnv, class C.jclass, method, sig string) C.jmethodID { + m := C.CString(method) + defer C.free(unsafe.Pointer(m)) + s := C.CString(sig) + defer C.free(unsafe.Pointer(s)) + jm := C.jni_GetStaticMethodID(env, class, m, s) + if err := exception(env); err != nil { + panic(err) + } + return jm +} + +//export Java_org_gioui_Gio_runGoMain +func Java_org_gioui_Gio_runGoMain(env *C.JNIEnv, class C.jclass, jdataDir C.jbyteArray, context C.jobject) { + initJVM(env, class, context) + dirBytes := C.jni_GetByteArrayElements(env, jdataDir) + if dirBytes == nil { + panic("runGoMain: GetByteArrayElements failed") + } + n := C.jni_GetArrayLength(env, jdataDir) + dataDir := C.GoStringN((*C.char)(unsafe.Pointer(dirBytes)), n) + dataDirChan <- dataDir + C.jni_ReleaseByteArrayElements(env, jdataDir, dirBytes) + + runMain() +} + +func initJVM(env *C.JNIEnv, gio C.jclass, ctx C.jobject) { + android.mu.Lock() + defer android.mu.Unlock() + if res := C.jni_GetJavaVM(env, &android.jvm); res != 0 { + panic("gio: GetJavaVM failed") + } + android.appCtx = C.jni_NewGlobalRef(env, ctx) + android.gioCls = C.jclass(C.jni_NewGlobalRef(env, C.jobject(gio))) + + cls := findClass(env, "android/view/accessibility/AccessibilityNodeInfo") + android.accessibilityNodeInfo.cls = C.jclass(C.jni_NewGlobalRef(env, C.jobject(cls))) + android.accessibilityNodeInfo.addChild = getMethodID(env, cls, "addChild", "(Landroid/view/View;I)V") + android.accessibilityNodeInfo.setBoundsInScreen = getMethodID(env, cls, "setBoundsInScreen", "(Landroid/graphics/Rect;)V") + android.accessibilityNodeInfo.setText = getMethodID(env, cls, "setText", "(Ljava/lang/CharSequence;)V") + android.accessibilityNodeInfo.setContentDescription = getMethodID(env, cls, "setContentDescription", "(Ljava/lang/CharSequence;)V") + android.accessibilityNodeInfo.setParent = getMethodID(env, cls, "setParent", "(Landroid/view/View;I)V") + android.accessibilityNodeInfo.addAction = getMethodID(env, cls, "addAction", "(I)V") + android.accessibilityNodeInfo.setClassName = getMethodID(env, cls, "setClassName", "(Ljava/lang/CharSequence;)V") + android.accessibilityNodeInfo.setCheckable = getMethodID(env, cls, "setCheckable", "(Z)V") + android.accessibilityNodeInfo.setSelected = getMethodID(env, cls, "setSelected", "(Z)V") + android.accessibilityNodeInfo.setChecked = getMethodID(env, cls, "setChecked", "(Z)V") + android.accessibilityNodeInfo.setEnabled = getMethodID(env, cls, "setEnabled", "(Z)V") + android.accessibilityNodeInfo.setAccessibilityFocused = getMethodID(env, cls, "setAccessibilityFocused", "(Z)V") + + cls = findClass(env, "android/graphics/Rect") + android.rect.cls = C.jclass(C.jni_NewGlobalRef(env, C.jobject(cls))) + android.rect.cons = getMethodID(env, cls, "<init>", "(IIII)V") + android.mwriteClipboard = getStaticMethodID(env, gio, "writeClipboard", "(Landroid/content/Context;Ljava/lang/String;)V") + android.mreadClipboard = getStaticMethodID(env, gio, "readClipboard", "(Landroid/content/Context;)Ljava/lang/String;") + android.mwakeupMainThread = getStaticMethodID(env, gio, "wakeupMainThread", "()V") + + intern := func(s string) C.jstring { + ref := C.jni_NewGlobalRef(env, C.jobject(javaString(env, s))) + return C.jstring(ref) + } + android.strings.androidViewView = intern("android.view.View") + android.strings.androidWidgetButton = intern("android.widget.Button") + android.strings.androidWidgetCheckBox = intern("android.widget.CheckBox") + android.strings.androidWidgetEditText = intern("android.widget.EditText") + android.strings.androidWidgetRadioButton = intern("android.widget.RadioButton") + android.strings.androidWidgetSwitch = intern("android.widget.Switch") +} + +// JavaVM returns the global JNI JavaVM. +func JavaVM() uintptr { + jvm := javaVM() + return uintptr(unsafe.Pointer(jvm)) +} + +func javaVM() *C.JavaVM { + android.mu.Lock() + defer android.mu.Unlock() + return android.jvm +} + +// AppContext returns the global Application context as a JNI jobject. +func AppContext() uintptr { + android.mu.Lock() + defer android.mu.Unlock() + return uintptr(android.appCtx) +} + +//export Java_org_gioui_GioView_onCreateView +func Java_org_gioui_GioView_onCreateView(env *C.JNIEnv, class C.jclass, view C.jobject) C.jlong { + gioView.once.Do(func() { + m := &gioView + m.getDensity = getMethodID(env, class, "getDensity", "()I") + m.getFontScale = getMethodID(env, class, "getFontScale", "()F") + m.showTextInput = getMethodID(env, class, "showTextInput", "()V") + m.hideTextInput = getMethodID(env, class, "hideTextInput", "()V") + m.setInputHint = getMethodID(env, class, "setInputHint", "(I)V") + m.postInvalidate = getMethodID(env, class, "postInvalidate", "()V") + m.invalidate = getMethodID(env, class, "invalidate", "()V") + m.setCursor = getMethodID(env, class, "setCursor", "(I)V") + m.setOrientation = getMethodID(env, class, "setOrientation", "(II)V") + m.setNavigationColor = getMethodID(env, class, "setNavigationColor", "(II)V") + m.setStatusColor = getMethodID(env, class, "setStatusColor", "(II)V") + m.setFullscreen = getMethodID(env, class, "setFullscreen", "(Z)V") + m.unregister = getMethodID(env, class, "unregister", "()V") + m.sendA11yEvent = getMethodID(env, class, "sendA11yEvent", "(II)V") + m.sendA11yChange = getMethodID(env, class, "sendA11yChange", "(I)V") + m.isA11yActive = getMethodID(env, class, "isA11yActive", "()Z") + }) + view = C.jni_NewGlobalRef(env, view) + wopts := <-mainWindow.out + w, ok := windows[wopts.window] + if !ok { + w = &window{ + callbacks: wopts.window, + } + windows[wopts.window] = w + } + if w.view != 0 { + w.detach(env) + } + w.view = view + w.callbacks.SetDriver(w) + handle := C.jlong(view) + views[handle] = w + w.loadConfig(env, class) + w.Configure(wopts.options) + w.setStage(system.StagePaused) + w.callbacks.Event(ViewEvent{View: uintptr(view)}) + return handle +} + +//export Java_org_gioui_GioView_onDestroyView +func Java_org_gioui_GioView_onDestroyView(env *C.JNIEnv, class C.jclass, handle C.jlong) { + w := views[handle] + w.detach(env) +} + +//export Java_org_gioui_GioView_onStopView +func Java_org_gioui_GioView_onStopView(env *C.JNIEnv, class C.jclass, handle C.jlong) { + w := views[handle] + w.started = false + w.setStage(system.StagePaused) +} + +//export Java_org_gioui_GioView_onStartView +func Java_org_gioui_GioView_onStartView(env *C.JNIEnv, class C.jclass, handle C.jlong) { + w := views[handle] + w.started = true + if w.win != nil { + w.setVisible(env) + } +} + +//export Java_org_gioui_GioView_onSurfaceDestroyed +func Java_org_gioui_GioView_onSurfaceDestroyed(env *C.JNIEnv, class C.jclass, handle C.jlong) { + w := views[handle] + w.win = nil + w.setStage(system.StagePaused) +} + +//export Java_org_gioui_GioView_onSurfaceChanged +func Java_org_gioui_GioView_onSurfaceChanged(env *C.JNIEnv, class C.jclass, handle C.jlong, surf C.jobject) { + w := views[handle] + w.win = C.ANativeWindow_fromSurface(env, surf) + if w.started { + w.setVisible(env) + } +} + +//export Java_org_gioui_GioView_onLowMemory +func Java_org_gioui_GioView_onLowMemory(env *C.JNIEnv, class C.jclass) { + runtime.GC() + debug.FreeOSMemory() +} + +//export Java_org_gioui_GioView_onConfigurationChanged +func Java_org_gioui_GioView_onConfigurationChanged(env *C.JNIEnv, class C.jclass, view C.jlong) { + w := views[view] + w.loadConfig(env, class) + if w.stage >= system.StageRunning { + w.draw(env, true) + } +} + +//export Java_org_gioui_GioView_onFrameCallback +func Java_org_gioui_GioView_onFrameCallback(env *C.JNIEnv, class C.jclass, view C.jlong) { + w, exist := views[view] + if !exist { + return + } + if w.stage < system.StageRunning { + return + } + if w.animating { + w.draw(env, false) + // Schedule the next draw immediately after this one. Since onFrameCallback runs + // on the UI thread, View.invalidate can be used here instead of postInvalidate. + callVoidMethod(env, w.view, gioView.invalidate) + } +} + +//export Java_org_gioui_GioView_onBack +func Java_org_gioui_GioView_onBack(env *C.JNIEnv, class C.jclass, view C.jlong) C.jboolean { + w := views[view] + ev := &system.CommandEvent{Type: system.CommandBack} + w.callbacks.Event(ev) + if ev.Cancel { + return C.JNI_TRUE + } + return C.JNI_FALSE +} + +//export Java_org_gioui_GioView_onFocusChange +func Java_org_gioui_GioView_onFocusChange(env *C.JNIEnv, class C.jclass, view C.jlong, focus C.jboolean) { + w := views[view] + w.callbacks.Event(key.FocusEvent{Focus: focus == C.JNI_TRUE}) +} + +//export Java_org_gioui_GioView_onWindowInsets +func Java_org_gioui_GioView_onWindowInsets(env *C.JNIEnv, class C.jclass, view C.jlong, top, right, bottom, left C.jint) { + w := views[view] + w.insets = system.Insets{ + Top: unit.Px(float32(top)), + Right: unit.Px(float32(right)), + Bottom: unit.Px(float32(bottom)), + Left: unit.Px(float32(left)), + } + if w.stage >= system.StageRunning { + w.draw(env, true) + } +} + +//export Java_org_gioui_GioView_initializeAccessibilityNodeInfo +func Java_org_gioui_GioView_initializeAccessibilityNodeInfo(env *C.JNIEnv, class C.jclass, view C.jlong, virtID, screenX, screenY C.jint, info C.jobject) C.jobject { + w := views[view] + semID := w.semIDFor(virtID) + sem, found := w.callbacks.LookupSemantic(semID) + if found { + off := f32.Pt(float32(screenX), float32(screenY)) + if err := w.initAccessibilityNodeInfo(env, sem, off, info); err != nil { + panic(err) + } + } + return info +} + +//export Java_org_gioui_GioView_onTouchExploration +func Java_org_gioui_GioView_onTouchExploration(env *C.JNIEnv, class C.jclass, view C.jlong, x, y C.jfloat) { + w := views[view] + semID, _ := w.callbacks.SemanticAt(f32.Pt(float32(x), float32(y))) + if w.semantic.hoverID == semID { + return + } + // Android expects ENTER before EXIT. + if semID != 0 { + callVoidMethod(env, w.view, gioView.sendA11yEvent, TYPE_VIEW_HOVER_ENTER, jvalue(w.virtualIDFor(semID))) + } + if prevID := w.semantic.hoverID; prevID != 0 { + callVoidMethod(env, w.view, gioView.sendA11yEvent, TYPE_VIEW_HOVER_EXIT, jvalue(w.virtualIDFor(prevID))) + } + w.semantic.hoverID = semID +} + +//export Java_org_gioui_GioView_onExitTouchExploration +func Java_org_gioui_GioView_onExitTouchExploration(env *C.JNIEnv, class C.jclass, view C.jlong) { + w := views[view] + if w.semantic.hoverID != 0 { + callVoidMethod(env, w.view, gioView.sendA11yEvent, TYPE_VIEW_HOVER_EXIT, jvalue(w.virtualIDFor(w.semantic.hoverID))) + w.semantic.hoverID = 0 + } +} + +//export Java_org_gioui_GioView_onA11yFocus +func Java_org_gioui_GioView_onA11yFocus(env *C.JNIEnv, class C.jclass, view C.jlong, virtID C.jint) { + w := views[view] + if semID := w.semIDFor(virtID); semID != w.semantic.focusID { + w.semantic.focusID = semID + // Android needs invalidate to refresh the TalkBack focus indicator. + callVoidMethod(env, w.view, gioView.invalidate) + } +} + +//export Java_org_gioui_GioView_onClearA11yFocus +func Java_org_gioui_GioView_onClearA11yFocus(env *C.JNIEnv, class C.jclass, view C.jlong, virtID C.jint) { + w := views[view] + if w.semantic.focusID == w.semIDFor(virtID) { + w.semantic.focusID = 0 + } +} + +func (w *window) initAccessibilityNodeInfo(env *C.JNIEnv, sem router.SemanticNode, off f32.Point, info C.jobject) error { + for _, ch := range sem.Children { + err := callVoidMethod(env, info, android.accessibilityNodeInfo.addChild, jvalue(w.view), jvalue(w.virtualIDFor(ch.ID))) + if err != nil { + return err + } + } + if sem.ParentID != 0 { + if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setParent, jvalue(w.view), jvalue(w.virtualIDFor(sem.ParentID))); err != nil { + return err + } + b := sem.Desc.Bounds.Add(off) + rect, err := newObject(env, android.rect.cls, android.rect.cons, + jvalue(b.Min.X), + jvalue(b.Min.Y), + jvalue(b.Max.X), + jvalue(b.Max.Y), + ) + if err != nil { + return err + } + if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setBoundsInScreen, jvalue(rect)); err != nil { + return err + } + } + d := sem.Desc + if l := d.Label; l != "" { + jlbl := javaString(env, l) + if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setText, jvalue(jlbl)); err != nil { + return err + } + } + if d.Description != "" { + jd := javaString(env, d.Description) + if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setContentDescription, jvalue(jd)); err != nil { + return err + } + } + addAction := func(act C.jint) { + if err := callVoidMethod(env, info, android.accessibilityNodeInfo.addAction, jvalue(act)); err != nil { + panic(err) + } + } + if d.Gestures&router.ClickGesture != 0 { + addAction(ACTION_CLICK) + } + clsName := android.strings.androidViewView + selectMethod := android.accessibilityNodeInfo.setChecked + checkable := false + switch d.Class { + case semantic.Button: + clsName = android.strings.androidWidgetButton + case semantic.CheckBox: + checkable = true + clsName = android.strings.androidWidgetCheckBox + case semantic.Editor: + clsName = android.strings.androidWidgetEditText + case semantic.RadioButton: + selectMethod = android.accessibilityNodeInfo.setSelected + clsName = android.strings.androidWidgetRadioButton + case semantic.Switch: + checkable = true + clsName = android.strings.androidWidgetSwitch + } + if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setClassName, jvalue(clsName)); err != nil { + panic(err) + } + if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setCheckable, jvalue(javaBool(checkable))); err != nil { + panic(err) + } + if err := callVoidMethod(env, info, selectMethod, jvalue(javaBool(d.Selected))); err != nil { + panic(err) + } + if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setEnabled, jvalue(javaBool(!d.Disabled))); err != nil { + panic(err) + } + isFocus := w.semantic.focusID == sem.ID + if err := callVoidMethod(env, info, android.accessibilityNodeInfo.setAccessibilityFocused, jvalue(javaBool(isFocus))); err != nil { + panic(err) + } + if isFocus { + addAction(ACTION_CLEAR_ACCESSIBILITY_FOCUS) + } else { + addAction(ACTION_ACCESSIBILITY_FOCUS) + } + return nil +} + +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 { + return HOST_VIEW_ID + } + return C.jint(id) +} + +func (w *window) semIDFor(virtID C.jint) router.SemanticID { + if virtID == HOST_VIEW_ID { + return w.semantic.rootID + } + return router.SemanticID(virtID) +} + +func (w *window) detach(env *C.JNIEnv) { + callVoidMethod(env, w.view, gioView.unregister) + w.callbacks.Event(ViewEvent{}) + w.callbacks.SetDriver(nil) + delete(views, C.jlong(w.view)) + C.jni_DeleteGlobalRef(env, w.view) + w.view = 0 +} + +func (w *window) setVisible(env *C.JNIEnv) { + width, height := C.ANativeWindow_getWidth(w.win), C.ANativeWindow_getHeight(w.win) + if width == 0 || height == 0 { + return + } + w.setStage(system.StageRunning) + 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 { + if C.ANativeWindow_setBuffersGeometry(w.win, 0, 0, C.int32_t(visID)) != 0 { + return errors.New("ANativeWindow_setBuffersGeometry failed") + } + return nil +} + +func (w *window) nativeWindow() (*C.ANativeWindow, int, int) { + width, height := C.ANativeWindow_getWidth(w.win), C.ANativeWindow_getHeight(w.win) + return w.win, int(width), int(height) +} + +func (w *window) loadConfig(env *C.JNIEnv, class C.jclass) { + dpi := int(C.jni_CallIntMethod(env, w.view, gioView.getDensity)) + w.fontScale = float32(C.jni_CallFloatMethod(env, w.view, gioView.getFontScale)) + switch dpi { + case C.ACONFIGURATION_DENSITY_NONE, + C.ACONFIGURATION_DENSITY_DEFAULT, + C.ACONFIGURATION_DENSITY_ANY: + // Assume standard density. + w.dpi = C.ACONFIGURATION_DENSITY_MEDIUM + default: + w.dpi = int(dpi) + } +} + +func (w *window) SetAnimating(anim bool) { + w.animating = anim + if anim { + runInJVM(javaVM(), func(env *C.JNIEnv) { + callVoidMethod(env, w.view, gioView.postInvalidate) + }) + } +} + +func (w *window) draw(env *C.JNIEnv, sync bool) { + size := image.Pt(int(C.ANativeWindow_getWidth(w.win)), int(C.ANativeWindow_getHeight(w.win))) + if size != w.config.Size { + w.config.Size = size + w.callbacks.Event(ConfigEvent{Config: w.config}) + } + if size.X == 0 || size.Y == 0 { + return + } + const inchPrDp = 1.0 / 160 + ppdp := float32(w.dpi) * inchPrDp + w.callbacks.Event(frameEvent{ + FrameEvent: system.FrameEvent{ + Now: time.Now(), + Size: w.config.Size, + Insets: w.insets, + Metric: unit.Metric{ + PxPerDp: ppdp, + PxPerSp: w.fontScale * ppdp, + }, + }, + Sync: sync, + }) + a11yActive, err := callBooleanMethod(env, w.view, gioView.isA11yActive) + if err != nil { + panic(err) + } + if a11yActive { + if newR, oldR := w.callbacks.SemanticRoot(), w.semantic.rootID; newR != oldR { + // Remap focus and hover. + if oldR == w.semantic.hoverID { + w.semantic.hoverID = newR + } + if oldR == w.semantic.focusID { + w.semantic.focusID = newR + } + w.semantic.rootID = newR + callVoidMethod(env, w.view, gioView.sendA11yChange, jvalue(w.virtualIDFor(newR))) + } + w.semantic.diffs = w.callbacks.AppendSemanticDiffs(w.semantic.diffs[:0]) + for _, id := range w.semantic.diffs { + callVoidMethod(env, w.view, gioView.sendA11yChange, jvalue(w.virtualIDFor(id))) + } + } +} + +type keyMapper func(devId, keyCode C.int32_t) rune + +func runInJVM(jvm *C.JavaVM, f func(env *C.JNIEnv)) { + if jvm == nil { + panic("nil JVM") + } + runtime.LockOSThread() + defer runtime.UnlockOSThread() + var env *C.JNIEnv + if res := C.jni_GetEnv(jvm, &env, C.JNI_VERSION_1_6); res != C.JNI_OK { + if res != C.JNI_EDETACHED { + panic(fmt.Errorf("JNI GetEnv failed with error %d", res)) + } + if C.jni_AttachCurrentThread(jvm, &env, nil) != C.JNI_OK { + panic(errors.New("runInJVM: AttachCurrentThread failed")) + } + defer C.jni_DetachCurrentThread(jvm) + } + + f(env) +} + +func convertKeyCode(code C.jint) (string, bool) { + var n string + switch code { + case C.AKEYCODE_DPAD_UP: + n = key.NameUpArrow + case C.AKEYCODE_DPAD_DOWN: + n = key.NameDownArrow + case C.AKEYCODE_DPAD_LEFT: + n = key.NameLeftArrow + case C.AKEYCODE_DPAD_RIGHT: + n = key.NameRightArrow + case C.AKEYCODE_FORWARD_DEL: + n = key.NameDeleteForward + case C.AKEYCODE_DEL: + n = key.NameDeleteBackward + case C.AKEYCODE_NUMPAD_ENTER: + n = key.NameEnter + case C.AKEYCODE_ENTER: + n = key.NameEnter + default: + return "", false + } + return n, true +} + +//export Java_org_gioui_GioView_onKeyEvent +func Java_org_gioui_GioView_onKeyEvent(env *C.JNIEnv, class C.jclass, handle C.jlong, keyCode, r C.jint, t C.jlong) { + w := views[handle] + if n, ok := convertKeyCode(keyCode); ok { + w.callbacks.Event(key.Event{Name: n}) + } + if r != 0 && r != '\n' { // Checking for "\n" to prevent duplication with key.NameEnter (gio#224). + w.callbacks.Event(key.EditEvent{Text: string(rune(r))}) + } +} + +//export Java_org_gioui_GioView_onTouchEvent +func Java_org_gioui_GioView_onTouchEvent(env *C.JNIEnv, class C.jclass, handle C.jlong, action, pointerID, tool C.jint, x, y, scrollX, scrollY C.jfloat, jbtns C.jint, t C.jlong) { + w := views[handle] + var typ pointer.Type + switch action { + case C.AMOTION_EVENT_ACTION_DOWN, C.AMOTION_EVENT_ACTION_POINTER_DOWN: + typ = pointer.Press + case C.AMOTION_EVENT_ACTION_UP, C.AMOTION_EVENT_ACTION_POINTER_UP: + typ = pointer.Release + case C.AMOTION_EVENT_ACTION_CANCEL: + typ = pointer.Cancel + case C.AMOTION_EVENT_ACTION_MOVE: + typ = pointer.Move + case C.AMOTION_EVENT_ACTION_SCROLL: + typ = pointer.Scroll + default: + return + } + var src pointer.Source + var btns pointer.Buttons + if jbtns&C.AMOTION_EVENT_BUTTON_PRIMARY != 0 { + btns |= pointer.ButtonPrimary + } + if jbtns&C.AMOTION_EVENT_BUTTON_SECONDARY != 0 { + btns |= pointer.ButtonSecondary + } + if jbtns&C.AMOTION_EVENT_BUTTON_TERTIARY != 0 { + btns |= pointer.ButtonTertiary + } + switch tool { + case C.AMOTION_EVENT_TOOL_TYPE_FINGER: + src = pointer.Touch + case C.AMOTION_EVENT_TOOL_TYPE_STYLUS: + src = pointer.Touch + case C.AMOTION_EVENT_TOOL_TYPE_MOUSE: + src = pointer.Mouse + case C.AMOTION_EVENT_TOOL_TYPE_UNKNOWN: + // For example, triggered via 'adb shell input tap'. + // Instead of discarding it, treat it as a touch event. + src = pointer.Touch + default: + return + } + w.callbacks.Event(pointer.Event{ + Type: typ, + Source: src, + Buttons: btns, + PointerID: pointer.ID(pointerID), + Time: time.Duration(t) * time.Millisecond, + Position: f32.Point{X: float32(x), Y: float32(y)}, + Scroll: f32.Pt(float32(scrollX), float32(scrollY)), + }) +} + +func (w *window) ShowTextInput(show bool) { + runInJVM(javaVM(), func(env *C.JNIEnv) { + if show { + callVoidMethod(env, w.view, gioView.showTextInput) + } else { + callVoidMethod(env, w.view, gioView.hideTextInput) + } + }) +} + +func (w *window) SetInputHint(mode key.InputHint) { + // Constants defined at https://developer.android.com/reference/android/text/InputType. + const ( + TYPE_NULL = 0 + TYPE_CLASS_NUMBER = 2 + TYPE_NUMBER_FLAG_DECIMAL = 8192 + TYPE_NUMBER_FLAG_SIGNED = 4096 + TYPE_TEXT_FLAG_NO_SUGGESTIONS = 524288 + TYPE_TEXT_VARIATION_VISIBLE_PASSWORD = 144 + ) + + runInJVM(javaVM(), func(env *C.JNIEnv) { + var m jvalue + switch mode { + case key.HintNumeric: + m = TYPE_CLASS_NUMBER | TYPE_NUMBER_FLAG_DECIMAL | TYPE_NUMBER_FLAG_SIGNED + default: + // TYPE_NULL, since TYPE_CLASS_TEXT isn't currently supported. + m = TYPE_NULL + } + + // The TYPE_TEXT_FLAG_NO_SUGGESTIONS and TYPE_TEXT_VARIATION_VISIBLE_PASSWORD are used to fix the + // Samsung keyboard compatibility, forcing to disable the suggests/auto-complete. gio#116. + m = m | TYPE_TEXT_FLAG_NO_SUGGESTIONS | TYPE_TEXT_VARIATION_VISIBLE_PASSWORD + + callVoidMethod(env, w.view, gioView.setInputHint, m) + }) +} + +func javaBool(b bool) C.jboolean { + if b { + return C.JNI_TRUE + } else { + return C.JNI_FALSE + } +} + +func javaString(env *C.JNIEnv, str string) C.jstring { + if str == "" { + return 0 + } + utf16Chars := utf16.Encode([]rune(str)) + return C.jni_NewString(env, (*C.jchar)(unsafe.Pointer(&utf16Chars[0])), C.int(len(utf16Chars))) +} + +func varArgs(args []jvalue) *C.jvalue { + if len(args) == 0 { + return nil + } + return (*C.jvalue)(unsafe.Pointer(&args[0])) +} + +func callStaticVoidMethod(env *C.JNIEnv, cls C.jclass, method C.jmethodID, args ...jvalue) error { + C.jni_CallStaticVoidMethodA(env, cls, method, varArgs(args)) + return exception(env) +} + +func callStaticObjectMethod(env *C.JNIEnv, cls C.jclass, method C.jmethodID, args ...jvalue) (C.jobject, error) { + res := C.jni_CallStaticObjectMethodA(env, cls, method, varArgs(args)) + return res, exception(env) +} + +func callVoidMethod(env *C.JNIEnv, obj C.jobject, method C.jmethodID, args ...jvalue) error { + C.jni_CallVoidMethodA(env, obj, method, varArgs(args)) + return exception(env) +} + +func callBooleanMethod(env *C.JNIEnv, obj C.jobject, method C.jmethodID, args ...jvalue) (bool, error) { + res := C.jni_CallBooleanMethodA(env, obj, method, varArgs(args)) + return res == C.JNI_TRUE, exception(env) +} + +func callObjectMethod(env *C.JNIEnv, obj C.jobject, method C.jmethodID, args ...jvalue) (C.jobject, error) { + res := C.jni_CallObjectMethodA(env, obj, method, varArgs(args)) + return res, exception(env) +} + +func newObject(env *C.JNIEnv, cls C.jclass, method C.jmethodID, args ...jvalue) (C.jobject, error) { + res := C.jni_NewObjectA(env, cls, method, varArgs(args)) + return res, exception(env) +} + +// exception returns an error corresponding to the pending +// exception, or nil if no exception is pending. The pending +// exception is cleared. +func exception(env *C.JNIEnv) error { + thr := C.jni_ExceptionOccurred(env) + if thr == 0 { + return nil + } + C.jni_ExceptionClear(env) + cls := getObjectClass(env, C.jobject(thr)) + toString := getMethodID(env, cls, "toString", "()Ljava/lang/String;") + msg, err := callObjectMethod(env, C.jobject(thr), toString) + if err != nil { + return err + } + return errors.New(goString(env, C.jstring(msg))) +} + +func getObjectClass(env *C.JNIEnv, obj C.jobject) C.jclass { + if obj == 0 { + panic("null object") + } + cls := C.jni_GetObjectClass(env, C.jobject(obj)) + if err := exception(env); err != nil { + // GetObjectClass should never fail. + panic(err) + } + return cls +} + +// goString converts the JVM jstring to a Go string. +func goString(env *C.JNIEnv, str C.jstring) string { + if str == 0 { + return "" + } + strlen := C.jni_GetStringLength(env, C.jstring(str)) + chars := C.jni_GetStringChars(env, C.jstring(str)) + var utf16Chars []uint16 + hdr := (*reflect.SliceHeader)(unsafe.Pointer(&utf16Chars)) + hdr.Data = uintptr(unsafe.Pointer(chars)) + hdr.Cap = int(strlen) + hdr.Len = int(strlen) + utf8 := utf16.Decode(utf16Chars) + return string(utf8) +} + +func findClass(env *C.JNIEnv, name string) C.jclass { + cn := C.CString(name) + defer C.free(unsafe.Pointer(cn)) + return C.jni_FindClass(env, cn) +} + +func osMain() { +} + +func newWindow(window *callbacks, options []Option) error { + mainWindow.in <- windowAndConfig{window, options} + return <-mainWindow.errs +} + +func (w *window) WriteClipboard(s string) { + runInJVM(javaVM(), func(env *C.JNIEnv) { + jstr := javaString(env, s) + callStaticVoidMethod(env, android.gioCls, android.mwriteClipboard, + jvalue(android.appCtx), jvalue(jstr)) + }) +} + +func (w *window) ReadClipboard() { + runInJVM(javaVM(), func(env *C.JNIEnv) { + c, err := callStaticObjectMethod(env, android.gioCls, android.mreadClipboard, + jvalue(android.appCtx)) + if err != nil { + return + } + content := goString(env, C.jstring(c)) + w.callbacks.Event(clipboard.Event{Text: content}) + }) +} + +func (w *window) Configure(options []Option) { + runInJVM(javaVM(), func(env *C.JNIEnv) { + prev := w.config + cnf := w.config + cnf.apply(unit.Metric{}, options) + 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 w.config != prev { + w.callbacks.Event(ConfigEvent{Config: w.config}) + } + }) +} + +func (w *window) Raise() {} + +func (w *window) SetCursor(name pointer.CursorName) { + runInJVM(javaVM(), func(env *C.JNIEnv) { + setCursor(env, w.view, name) + }) +} + +func (w *window) Wakeup() { + runOnMain(func(env *C.JNIEnv) { + w.callbacks.Event(wakeupEvent{}) + }) +} + +func setCursor(env *C.JNIEnv, view C.jobject, name pointer.CursorName) { + var curID int + switch name { + default: + fallthrough + case pointer.CursorDefault: + curID = 1000 // TYPE_ARROW + case pointer.CursorText: + curID = 1008 // TYPE_TEXT + case pointer.CursorPointer: + curID = 1002 // TYPE_HAND + case pointer.CursorCrossHair: + curID = 1007 // TYPE_CROSSHAIR + case pointer.CursorColResize: + curID = 1014 // TYPE_HORIZONTAL_DOUBLE_ARROW + case pointer.CursorRowResize: + curID = 1015 // TYPE_VERTICAL_DOUBLE_ARROW + case pointer.CursorNone: + curID = 0 // TYPE_NULL + } + callVoidMethod(env, view, gioView.setCursor, jvalue(curID)) +} + +func setOrientation(env *C.JNIEnv, view C.jobject, mode Orientation) { + var ( + id int + idFallback int // Used only for SDK 17 or older. + ) + // Constants defined at https://developer.android.com/reference/android/content/pm/ActivityInfo. + switch mode { + case AnyOrientation: + id, idFallback = 2, 2 // SCREEN_ORIENTATION_USER + case LandscapeOrientation: + id, idFallback = 11, 0 // SCREEN_ORIENTATION_USER_LANDSCAPE (or SCREEN_ORIENTATION_LANDSCAPE) + case PortraitOrientation: + id, idFallback = 12, 1 // SCREEN_ORIENTATION_USER_PORTRAIT (or SCREEN_ORIENTATION_PORTRAIT) + } + callVoidMethod(env, view, gioView.setOrientation, jvalue(id), jvalue(idFallback)) +} + +func setStatusColor(env *C.JNIEnv, view C.jobject, color color.NRGBA) { + callVoidMethod(env, view, gioView.setStatusColor, + jvalue(uint32(color.A)<<24|uint32(color.R)<<16|uint32(color.G)<<8|uint32(color.B)), + jvalue(int(f32color.LinearFromSRGB(color).Luminance()*255)), + ) +} + +func setNavigationColor(env *C.JNIEnv, view C.jobject, color color.NRGBA) { + callVoidMethod(env, view, gioView.setNavigationColor, + jvalue(uint32(color.A)<<24|uint32(color.R)<<16|uint32(color.G)<<8|uint32(color.B)), + jvalue(int(f32color.LinearFromSRGB(color).Luminance()*255)), + ) +} + +// Close the window. Not implemented for Android. +func (w *window) Close() {} + +// Maximize maximizes the window. Not implemented for Android. +func (w *window) Maximize() {} + +// Center the window. Not implemented for Android. +func (w *window) Center() {} + +// runOnMain runs a function on the Java main thread. +func runOnMain(f func(env *C.JNIEnv)) { + go func() { + mainFuncs <- f + runInJVM(javaVM(), func(env *C.JNIEnv) { + callStaticVoidMethod(env, android.gioCls, android.mwakeupMainThread) + }) + }() +} + +//export Java_org_gioui_Gio_scheduleMainFuncs +func Java_org_gioui_Gio_scheduleMainFuncs(env *C.JNIEnv, cls C.jclass) { + for { + select { + case f := <-mainFuncs: + f(env) + default: + return + } + } +} + +func (_ ViewEvent) ImplementsEvent() {} diff --git a/vendor/gioui.org/app/internal/window/os_darwin.go b/vendor/gioui.org/app/os_darwin.go index a3b77dc..c0554db 100644 --- a/vendor/gioui.org/app/internal/window/os_darwin.go +++ b/vendor/gioui.org/app/os_darwin.go @@ -1,18 +1,33 @@ // SPDX-License-Identifier: Unlicense OR MIT -package window +package app /* #include <Foundation/Foundation.h> __attribute__ ((visibility ("hidden"))) void gio_wakeupMainThread(void); -__attribute__ ((visibility ("hidden"))) NSUInteger gio_nsstringLength(CFTypeRef str); -__attribute__ ((visibility ("hidden"))) void gio_nsstringGetCharacters(CFTypeRef str, unichar *chars, NSUInteger loc, NSUInteger length); __attribute__ ((visibility ("hidden"))) CFTypeRef gio_createDisplayLink(void); __attribute__ ((visibility ("hidden"))) void gio_releaseDisplayLink(CFTypeRef dl); __attribute__ ((visibility ("hidden"))) int gio_startDisplayLink(CFTypeRef dl); __attribute__ ((visibility ("hidden"))) int gio_stopDisplayLink(CFTypeRef dl); __attribute__ ((visibility ("hidden"))) void gio_setDisplayLinkDisplay(CFTypeRef dl, uint64_t did); +__attribute__ ((visibility ("hidden"))) void gio_hideCursor(); +__attribute__ ((visibility ("hidden"))) void gio_showCursor(); +__attribute__ ((visibility ("hidden"))) void gio_setCursor(NSUInteger curID); + +static bool isMainThread() { + return [NSThread isMainThread]; +} + +static NSUInteger nsstringLength(CFTypeRef cstr) { + NSString *str = (__bridge NSString *)cstr; + return [str length]; +} + +static void nsstringGetCharacters(CFTypeRef cstr, unichar *chars, NSUInteger loc, NSUInteger length) { + NSString *str = (__bridge NSString *)cstr; + [str getCharacters:chars range:NSMakeRange(loc, length)]; +} */ import "C" import ( @@ -22,6 +37,8 @@ import ( "time" "unicode/utf16" "unsafe" + + "gioui.org/io/pointer" ) // displayLink is the state for a display link (CVDisplayLinkRef on macOS, @@ -50,6 +67,10 @@ var mainFuncs = make(chan func(), 1) // runOnMain runs the function on the main thread. func runOnMain(f func()) { + if C.isMainThread() { + f() + return + } go func() { mainFuncs <- f C.gio_wakeupMainThread() @@ -71,13 +92,16 @@ func gio_dispatchMainFuncs() { // nsstringToString converts a NSString to a Go string, and // releases the original string. func nsstringToString(str C.CFTypeRef) string { + if str == 0 { + return "" + } defer C.CFRelease(str) - n := C.gio_nsstringLength(str) + n := C.nsstringLength(str) if n == 0 { return "" } chars := make([]uint16, n) - C.gio_nsstringGetCharacters(str, (*C.unichar)(unsafe.Pointer(&chars[0])), 0, n) + C.nsstringGetCharacters(str, (*C.unichar)(unsafe.Pointer(&chars[0])), 0, n) utf8 := utf16.Decode(chars) return string(utf8) } @@ -169,3 +193,45 @@ func gio_onFrameCallback(dl C.CFTypeRef) { } } } + +// windowSetCursor updates the cursor from the current one to a new one +// and returns the new one. +func windowSetCursor(from, to pointer.CursorName) pointer.CursorName { + if from == to { + return to + } + var curID int + switch to { + default: + to = pointer.CursorDefault + fallthrough + case pointer.CursorDefault: + curID = 1 + case pointer.CursorText: + curID = 2 + case pointer.CursorPointer: + curID = 3 + case pointer.CursorCrossHair: + curID = 4 + case pointer.CursorColResize: + curID = 5 + case pointer.CursorRowResize: + curID = 6 + case pointer.CursorGrab: + curID = 7 + case pointer.CursorNone: + C.gio_hideCursor() + return to + } + if from == pointer.CursorNone { + C.gio_showCursor() + } + C.gio_setCursor(C.NSUInteger(curID)) + return to +} + +func (w *window) Wakeup() { + runOnMain(func() { + w.w.Event(wakeupEvent{}) + }) +} diff --git a/vendor/gioui.org/app/os_darwin.m b/vendor/gioui.org/app/os_darwin.m new file mode 100644 index 0000000..772cc28 --- /dev/null +++ b/vendor/gioui.org/app/os_darwin.m @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +@import Dispatch; +@import Foundation; + +#include "_cgo_export.h" + +void gio_wakeupMainThread(void) { + dispatch_async(dispatch_get_main_queue(), ^{ + gio_dispatchMainFuncs(); + }); +} diff --git a/vendor/gioui.org/app/internal/window/os_ios.go b/vendor/gioui.org/app/os_ios.go index 1865454..f6f6dea 100644 --- a/vendor/gioui.org/app/internal/window/os_ios.go +++ b/vendor/gioui.org/app/os_ios.go @@ -1,8 +1,9 @@ // SPDX-License-Identifier: Unlicense OR MIT +//go:build darwin && ios // +build darwin,ios -package window +package app /* #cgo CFLAGS: -DGLES_SILENCE_DEPRECATION -Werror -Wno-deprecated-declarations -fmodules -fobjc-arc -x objective-c @@ -17,14 +18,55 @@ struct drawParams { CGFloat top, right, bottom, left; }; -__attribute__ ((visibility ("hidden"))) void gio_showTextInput(CFTypeRef viewRef); -__attribute__ ((visibility ("hidden"))) void gio_hideTextInput(CFTypeRef viewRef); -__attribute__ ((visibility ("hidden"))) void gio_addLayerToView(CFTypeRef viewRef, CFTypeRef layerRef); -__attribute__ ((visibility ("hidden"))) void gio_updateView(CFTypeRef viewRef, CFTypeRef layerRef); -__attribute__ ((visibility ("hidden"))) void gio_removeLayer(CFTypeRef layerRef); -__attribute__ ((visibility ("hidden"))) struct drawParams gio_viewDrawParams(CFTypeRef viewRef); -__attribute__ ((visibility ("hidden"))) CFTypeRef gio_readClipboard(void); -__attribute__ ((visibility ("hidden"))) void gio_writeClipboard(unichar *chars, NSUInteger length); +static void writeClipboard(unichar *chars, NSUInteger length) { + @autoreleasepool { + NSString *s = [NSString string]; + if (length > 0) { + s = [NSString stringWithCharacters:chars length:length]; + } + UIPasteboard *p = UIPasteboard.generalPasteboard; + p.string = s; + } +} + +static CFTypeRef readClipboard(void) { + @autoreleasepool { + UIPasteboard *p = UIPasteboard.generalPasteboard; + return (__bridge_retained CFTypeRef)p.string; + } +} + +static void showTextInput(CFTypeRef viewRef) { + UIView *view = (__bridge UIView *)viewRef; + [view becomeFirstResponder]; +} + +static void hideTextInput(CFTypeRef viewRef) { + UIView *view = (__bridge UIView *)viewRef; + [view resignFirstResponder]; +} + +static struct drawParams viewDrawParams(CFTypeRef viewRef) { + UIView *v = (__bridge UIView *)viewRef; + struct drawParams params; + CGFloat scale = v.layer.contentsScale; + // Use 163 as the standard ppi on iOS. + params.dpi = 163*scale; + params.sdpi = params.dpi; + UIEdgeInsets insets = v.layoutMargins; + if (@available(iOS 11.0, tvOS 11.0, *)) { + UIFontMetrics *metrics = [UIFontMetrics defaultMetrics]; + params.sdpi = [metrics scaledValueForValue:params.sdpi]; + insets = v.safeAreaInsets; + } + params.width = v.bounds.size.width*scale; + params.height = v.bounds.size.height*scale; + params.top = insets.top*scale; + params.right = insets.right*scale; + params.bottom = insets.bottom*scale; + params.left = insets.left*scale; + return params; +} */ import "C" @@ -32,33 +74,36 @@ import ( "image" "runtime" "runtime/debug" - "sync/atomic" "time" "unicode/utf16" "unsafe" "gioui.org/f32" + "gioui.org/io/clipboard" "gioui.org/io/key" "gioui.org/io/pointer" "gioui.org/io/system" "gioui.org/unit" ) +type ViewEvent struct { + // ViewController is a CFTypeRef for the UIViewController backing a Window. + ViewController uintptr +} + type window struct { view C.CFTypeRef - w Callbacks + w *callbacks displayLink *displayLink - layer C.CFTypeRef - visible atomic.Value + visible bool + cursor pointer.CursorName pointerMap []C.CFTypeRef } var mainWindow = newWindowRendezvous() -var layerFactory func() uintptr - var views = make(map[C.CFTypeRef]*window) func init() { @@ -67,7 +112,7 @@ func init() { } //export onCreate -func onCreate(view C.CFTypeRef) { +func onCreate(view, controller C.CFTypeRef) { w := &window{ view: view, } @@ -81,11 +126,9 @@ func onCreate(view C.CFTypeRef) { wopts := <-mainWindow.out w.w = wopts.window w.w.SetDriver(w) - w.visible.Store(false) - w.layer = C.CFTypeRef(layerFactory()) - C.gio_addLayerToView(view, w.layer) views[view] = w w.w.Event(system.StageEvent{Stage: system.StagePaused}) + w.w.Event(ViewEvent{ViewController: uintptr(controller)}) } //export gio_onDraw @@ -95,18 +138,17 @@ func gio_onDraw(view C.CFTypeRef) { } func (w *window) draw(sync bool) { - params := C.gio_viewDrawParams(w.view) + params := C.viewDrawParams(w.view) if params.width == 0 || params.height == 0 { return } - wasVisible := w.isVisible() - w.visible.Store(true) - C.gio_updateView(w.view, w.layer) + wasVisible := w.visible + w.visible = true if !wasVisible { w.w.Event(system.StageEvent{Stage: system.StageRunning}) } const inchPrDp = 1.0 / 163 - w.w.Event(FrameEvent{ + w.w.Event(frameEvent{ FrameEvent: system.FrameEvent{ Now: time.Now(), Size: image.Point{ @@ -131,7 +173,7 @@ func (w *window) draw(sync bool) { //export onStop func onStop(view C.CFTypeRef) { w := views[view] - w.visible.Store(false) + w.visible = false w.w.Event(system.StageEvent{Stage: system.StagePaused}) } @@ -139,11 +181,9 @@ func onStop(view C.CFTypeRef) { func onDestroy(view C.CFTypeRef) { w := views[view] delete(views, view) + w.w.Event(ViewEvent{}) w.w.Event(system.DestroyEvent{}) w.displayLink.Close() - C.gio_removeLayer(w.layer) - C.CFRelease(w.layer) - w.layer = 0 w.view = 0 } @@ -220,23 +260,23 @@ func onTouch(last C.int, view, touchRef C.CFTypeRef, phase C.NSInteger, x, y C.C } func (w *window) ReadClipboard() { - runOnMain(func() { - content := nsstringToString(C.gio_readClipboard()) - w.w.Event(system.ClipboardEvent{Text: content}) - }) + content := nsstringToString(C.readClipboard()) + w.w.Event(clipboard.Event{Text: content}) } func (w *window) WriteClipboard(s string) { u16 := utf16.Encode([]rune(s)) - runOnMain(func() { - var chars *C.unichar - if len(u16) > 0 { - chars = (*C.unichar)(unsafe.Pointer(&u16[0])) - } - C.gio_writeClipboard(chars, C.NSUInteger(len(u16))) - }) + var chars *C.unichar + if len(u16) > 0 { + chars = (*C.unichar)(unsafe.Pointer(&u16[0])) + } + C.writeClipboard(chars, C.NSUInteger(len(u16))) } +func (w *window) Configure([]Option) {} + +func (w *window) Raise() {} + func (w *window) SetAnimating(anim bool) { v := w.view if v == 0 { @@ -249,6 +289,10 @@ func (w *window) SetAnimating(anim bool) { } } +func (w *window) SetCursor(name pointer.CursorName) { + w.cursor = windowSetCursor(w.cursor, name) +} + func (w *window) onKeyCommand(name string) { w.w.Event(key.Event{ Name: name, @@ -275,42 +319,40 @@ func (w *window) lookupTouch(last bool, touch C.CFTypeRef) pointer.ID { return pointer.ID(id) } -func (w *window) contextLayer() uintptr { - return uintptr(w.layer) -} - -func (w *window) isVisible() bool { - return w.visible.Load().(bool) +func (w *window) contextView() C.CFTypeRef { + return w.view } func (w *window) ShowTextInput(show bool) { - v := w.view - if v == 0 { - return + if show { + C.showTextInput(w.view) + } else { + C.hideTextInput(w.view) } - C.CFRetain(v) - runOnMain(func() { - defer C.CFRelease(v) - if show { - C.gio_showTextInput(w.view) - } else { - C.gio_hideTextInput(w.view) - } - }) } +func (w *window) SetInputHint(_ key.InputHint) {} + // Close the window. Not implemented for iOS. func (w *window) Close() {} -func NewWindow(win Callbacks, opts *Options) error { - mainWindow.in <- windowAndOptions{win, opts} +// Maximize the window. Not implemented for iOS. +func (w *window) Maximize() {} + +// Center the window. Not implemented for iOS. +func (w *window) Center() {} + +func newWindow(win *callbacks, options []Option) error { + mainWindow.in <- windowAndConfig{win, options} return <-mainWindow.errs } -func Main() { +func osMain() { } //export gio_runMain func gio_runMain() { runMain() } + +func (_ ViewEvent) ImplementsEvent() {} diff --git a/vendor/gioui.org/app/internal/window/os_ios.m b/vendor/gioui.org/app/os_ios.m index 66830e4..94f254b 100644 --- a/vendor/gioui.org/app/internal/window/os_ios.m +++ b/vendor/gioui.org/app/os_ios.m @@ -8,6 +8,8 @@ #include "_cgo_export.h" #include "framework_ios.h" +__attribute__ ((visibility ("hidden"))) Class gio_layerClass(void); + @interface GioView: UIView <UIKeyInput> @end @@ -28,7 +30,7 @@ CGFloat _keyboardHeight; #endif drawView.preservesSuperviewLayoutMargins = YES; drawView.layoutMargins = UIEdgeInsetsMake(0, 0, 0, 0); - onCreate((__bridge CFTypeRef)drawView); + onCreate((__bridge CFTypeRef)drawView, (__bridge CFTypeRef)self); [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChange:) name:UIKeyboardWillShowNotification @@ -125,6 +127,9 @@ NSArray<UIKeyCommand *> *_keyCommands; gio_onFrameCallback((__bridge CFTypeRef)link); } ++ (Class)layerClass { + return gio_layerClass(); +} - (void)willMoveToWindow:(UIWindow *)newWindow { if (self.window != nil) { [[NSNotificationCenter defaultCenter] removeObserver:self @@ -157,9 +162,6 @@ NSArray<UIKeyCommand *> *_keyCommands; } } -- (void)dealloc { -} - - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { handleTouches(0, self, touches, event); } @@ -229,74 +231,6 @@ NSArray<UIKeyCommand *> *_keyCommands; } @end -void gio_writeClipboard(unichar *chars, NSUInteger length) { - @autoreleasepool { - NSString *s = [NSString string]; - if (length > 0) { - s = [NSString stringWithCharacters:chars length:length]; - } - UIPasteboard *p = UIPasteboard.generalPasteboard; - p.string = s; - } -} - -CFTypeRef gio_readClipboard(void) { - @autoreleasepool { - UIPasteboard *p = UIPasteboard.generalPasteboard; - return (__bridge_retained CFTypeRef)p.string; - } -} - -void gio_showTextInput(CFTypeRef viewRef) { - UIView *view = (__bridge UIView *)viewRef; - [view becomeFirstResponder]; -} - -void gio_hideTextInput(CFTypeRef viewRef) { - UIView *view = (__bridge UIView *)viewRef; - [view resignFirstResponder]; -} - -void gio_addLayerToView(CFTypeRef viewRef, CFTypeRef layerRef) { - UIView *view = (__bridge UIView *)viewRef; - CALayer *layer = (__bridge CALayer *)layerRef; - [view.layer addSublayer:layer]; -} - -void gio_updateView(CFTypeRef viewRef, CFTypeRef layerRef) { - UIView *view = (__bridge UIView *)viewRef; - CAEAGLLayer *layer = (__bridge CAEAGLLayer *)layerRef; - layer.contentsScale = view.contentScaleFactor; - layer.bounds = view.bounds; -} - -void gio_removeLayer(CFTypeRef layerRef) { - CALayer *layer = (__bridge CALayer *)layerRef; - [layer removeFromSuperlayer]; -} - -struct drawParams gio_viewDrawParams(CFTypeRef viewRef) { - UIView *v = (__bridge UIView *)viewRef; - struct drawParams params; - CGFloat scale = v.layer.contentsScale; - // Use 163 as the standard ppi on iOS. - params.dpi = 163*scale; - params.sdpi = params.dpi; - UIEdgeInsets insets = v.layoutMargins; - if (@available(iOS 11.0, tvOS 11.0, *)) { - UIFontMetrics *metrics = [UIFontMetrics defaultMetrics]; - params.sdpi = [metrics scaledValueForValue:params.sdpi]; - insets = v.safeAreaInsets; - } - params.width = v.bounds.size.width*scale; - params.height = v.bounds.size.height*scale; - params.top = insets.top*scale; - params.right = insets.right*scale; - params.bottom = insets.bottom*scale; - params.left = insets.left*scale; - return params; -} - CFTypeRef gio_createDisplayLink(void) { CADisplayLink *dl = [CADisplayLink displayLinkWithTarget:[GioView class] selector:@selector(onFrameCallback:)]; dl.paused = YES; @@ -326,3 +260,15 @@ void gio_releaseDisplayLink(CFTypeRef dlref) { void gio_setDisplayLinkDisplay(CFTypeRef dl, uint64_t did) { // Nothing to do on iOS. } + +void gio_hideCursor() { + // Not supported. +} + +void gio_showCursor() { + // Not supported. +} + +void gio_setCursor(NSUInteger curID) { + // Not supported. +} diff --git a/vendor/gioui.org/app/internal/window/os_js.go b/vendor/gioui.org/app/os_js.go index 32235df..5b1388c 100644 --- a/vendor/gioui.org/app/internal/window/os_js.go +++ b/vendor/gioui.org/app/os_js.go @@ -1,42 +1,62 @@ // SPDX-License-Identifier: Unlicense OR MIT -package window +package app import ( + "fmt" "image" + "image/color" "strings" - "sync" "syscall/js" "time" "unicode" "unicode/utf8" + "gioui.org/internal/f32color" + "gioui.org/f32" + "gioui.org/io/clipboard" "gioui.org/io/key" "gioui.org/io/pointer" "gioui.org/io/system" "gioui.org/unit" ) +type ViewEvent struct{} + type window struct { window js.Value + document js.Value + head js.Value clipboard js.Value cnv js.Value tarea js.Value - w Callbacks + w *callbacks redraw js.Func clipboardCallback js.Func requestAnimationFrame js.Value + browserHistory js.Value + visualViewport js.Value + screenOrientation js.Value cleanfuncs []func() touches []js.Value composing bool + requestFocus bool + + chanAnimation chan struct{} + chanRedraw chan struct{} - mu sync.Mutex + config Config + inset f32.Point scale float32 animating bool + // animRequested tracks whether a requestAnimationFrame callback + // is pending. + animRequested bool + wakeups chan struct{} } -func NewWindow(win Callbacks, opts *Options) error { +func newWindow(win *callbacks, options []Option) error { doc := js.Global().Get("document") cont := getContainer(doc) cnv := createCanvas(doc) @@ -45,29 +65,55 @@ func NewWindow(win Callbacks, opts *Options) error { cont.Call("appendChild", tarea) w := &window{ cnv: cnv, + document: doc, tarea: tarea, window: js.Global().Get("window"), + head: doc.Get("head"), clipboard: js.Global().Get("navigator").Get("clipboard"), + wakeups: make(chan struct{}, 1), } w.requestAnimationFrame = w.window.Get("requestAnimationFrame") + w.browserHistory = w.window.Get("history") + w.visualViewport = w.window.Get("visualViewport") + if w.visualViewport.IsUndefined() { + w.visualViewport = w.window + } + if screen := w.window.Get("screen"); screen.Truthy() { + 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.animCallback() + w.chanAnimation <- struct{}{} return nil }) w.clipboardCallback = w.funcOf(func(this js.Value, args []js.Value) interface{} { content := args[0].String() - win.Event(system.ClipboardEvent{Text: content}) + go win.Event(clipboard.Event{Text: content}) return nil }) w.addEventListeners() + w.addHistory() w.w = win + go func() { + defer w.cleanup() w.w.SetDriver(w) - w.focus() + w.Configure(options) + w.blur() w.w.Event(system.StageEvent{Stage: system.StageRunning}) + w.resize() w.draw(true) - select {} - w.cleanup() + for { + select { + case <-w.wakeups: + w.w.Event(wakeupEvent{}) + case <-w.chanAnimation: + w.animCallback() + case <-w.chanRedraw: + w.draw(true) + } + } }() return nil } @@ -116,8 +162,33 @@ func (w *window) cleanup() { } func (w *window) addEventListeners() { - w.addEventListener(w.window, "resize", func(this js.Value, args []js.Value) interface{} { - w.draw(true) + w.addEventListener(w.visualViewport, "resize", func(this js.Value, args []js.Value) interface{} { + w.resize() + w.chanRedraw <- struct{}{} + return nil + }) + w.addEventListener(w.window, "contextmenu", func(this js.Value, args []js.Value) interface{} { + args[0].Call("preventDefault") + return nil + }) + w.addEventListener(w.window, "popstate", func(this js.Value, args []js.Value) interface{} { + ev := &system.CommandEvent{Type: system.CommandBack} + w.w.Event(ev) + if ev.Cancel { + return w.browserHistory.Call("forward") + } + + 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{} { @@ -126,6 +197,10 @@ func (w *window) addEventListeners() { }) w.addEventListener(w.cnv, "mousedown", func(this js.Value, args []js.Value) interface{} { w.pointerEvent(pointer.Press, 0, 0, args[0]) + if w.requestFocus { + w.focus() + w.requestFocus = false + } return nil }) w.addEventListener(w.cnv, "mouseup", func(this js.Value, args []js.Value) interface{} { @@ -149,6 +224,10 @@ func (w *window) addEventListeners() { }) w.addEventListener(w.cnv, "touchstart", func(this js.Value, args []js.Value) interface{} { w.touchEvent(pointer.Press, args[0]) + if w.requestFocus { + w.focus() // iOS can only focus inside a Touch event. + w.requestFocus = false + } return nil }) w.addEventListener(w.cnv, "touchend", func(this js.Value, args []js.Value) interface{} { @@ -177,10 +256,15 @@ func (w *window) addEventListeners() { }) w.addEventListener(w.tarea, "blur", func(this js.Value, args []js.Value) interface{} { w.w.Event(key.FocusEvent{Focus: false}) + w.blur() return nil }) w.addEventListener(w.tarea, "keydown", func(this js.Value, args []js.Value) interface{} { - w.keyEvent(args[0]) + w.keyEvent(args[0], key.Press) + return nil + }) + w.addEventListener(w.tarea, "keyup", func(this js.Value, args []js.Value) interface{} { + w.keyEvent(args[0], key.Release) return nil }) w.addEventListener(w.tarea, "compositionstart", func(this js.Value, args []js.Value) interface{} { @@ -199,6 +283,18 @@ func (w *window) addEventListeners() { w.flushInput() return nil }) + w.addEventListener(w.tarea, "paste", func(this js.Value, args []js.Value) interface{} { + if w.clipboard.IsUndefined() { + return nil + } + // Prevents duplicated-paste, since "paste" is already handled through Clipboard API. + args[0].Call("preventDefault") + return nil + }) +} + +func (w *window) addHistory() { + w.browserHistory.Call("pushState", nil, nil, w.window.Get("location").Get("href")) } func (w *window) flushInput() { @@ -209,18 +305,42 @@ func (w *window) flushInput() { func (w *window) blur() { w.tarea.Call("blur") + w.requestFocus = false } func (w *window) focus() { w.tarea.Call("focus") + w.requestFocus = true +} + +func (w *window) keyboard(hint key.InputHint) { + var m string + switch hint { + case key.HintAny: + m = "text" + case key.HintText: + m = "text" + case key.HintNumeric: + m = "decimal" + case key.HintEmail: + m = "email" + case key.HintURL: + m = "url" + case key.HintTelephone: + m = "tel" + default: + m = "text" + } + w.tarea.Set("inputMode", m) } -func (w *window) keyEvent(e js.Value) { +func (w *window) keyEvent(e js.Value, ks key.State) { k := e.Get("key").String() if n, ok := translateKey(k); ok { cmd := key.Event{ Name: n, Modifiers: modifiersFor(e), + State: ks, } w.w.Event(cmd) } @@ -230,6 +350,10 @@ func (w *window) keyEvent(e js.Value) { // KeyEvent. func modifiersFor(e js.Value) key.Modifiers { var mods key.Modifiers + if e.Get("getModifierState").IsUndefined() { + // Some browsers doesn't support getModifierState. + return mods + } if e.Call("getModifierState", "Alt").Bool() { mods |= key.ModAlt } @@ -248,9 +372,7 @@ func (w *window) touchEvent(typ pointer.Type, e js.Value) { changedTouches := e.Get("changedTouches") n := changedTouches.Length() rect := w.cnv.Call("getBoundingClientRect") - w.mu.Lock() scale := w.scale - w.mu.Unlock() var mods key.Modifiers if e.Get("shiftKey").Bool() { mods |= key.ModShift @@ -277,7 +399,7 @@ func (w *window) touchEvent(typ pointer.Type, e js.Value) { Position: pos, PointerID: pid, Time: t, - Modifiers: modifiersFor(e), + Modifiers: mods, }) } } @@ -300,9 +422,7 @@ func (w *window) pointerEvent(typ pointer.Type, dx, dy float32, e js.Value) { rect := w.cnv.Call("getBoundingClientRect") x -= rect.Get("left").Float() y -= rect.Get("top").Float() - w.mu.Lock() scale := w.scale - w.mu.Unlock() pos := f32.Point{ X: float32(x) * scale, Y: float32(y) * scale, @@ -315,13 +435,13 @@ func (w *window) pointerEvent(typ pointer.Type, dx, dy float32, e js.Value) { jbtns := e.Get("buttons").Int() var btns pointer.Buttons if jbtns&1 != 0 { - btns |= pointer.ButtonLeft + btns |= pointer.ButtonPrimary } if jbtns&2 != 0 { - btns |= pointer.ButtonRight + btns |= pointer.ButtonSecondary } if jbtns&4 != 0 { - btns |= pointer.ButtonMiddle + btns |= pointer.ButtonTertiary } w.w.Event(pointer.Event{ Type: typ, @@ -343,7 +463,7 @@ func (w *window) addEventListener(this js.Value, event string, f func(this js.Va } // funcOf is like js.FuncOf but adds the js.Func to a list of -// functions to be released up. +// functions to be released during cleanup. func (w *window) funcOf(f func(this js.Value, args []js.Value) interface{}) js.Func { jsf := js.FuncOf(f) w.cleanfuncs = append(w.cleanfuncs, jsf.Release) @@ -351,24 +471,22 @@ func (w *window) funcOf(f func(this js.Value, args []js.Value) interface{}) js.F } func (w *window) animCallback() { - w.mu.Lock() anim := w.animating + w.animRequested = anim if anim { w.requestAnimationFrame.Invoke(w.redraw) } - w.mu.Unlock() if anim { w.draw(false) } } func (w *window) SetAnimating(anim bool) { - w.mu.Lock() - defer w.mu.Unlock() - if anim && !w.animating { + w.animating = anim + if anim && !w.animRequested { + w.animRequested = true w.requestAnimationFrame.Invoke(w.redraw) } - w.animating = anim } func (w *window) ReadClipboard() { @@ -391,6 +509,44 @@ func (w *window) WriteClipboard(s string) { w.clipboard.Call("writeText", s) } +func (w *window) Configure(options []Option) { + prev := w.config + cnf := w.config + cnf.apply(unit.Metric{}, options) + if prev.Title != cnf.Title { + w.config.Title = cnf.Title + w.document.Set("title", cnf.Title) + } + if prev.Mode != cnf.Mode { + w.windowMode(cnf.Mode) + } + if prev.NavigationColor != cnf.NavigationColor { + w.config.NavigationColor = cnf.NavigationColor + w.navigationColor(cnf.NavigationColor) + } + if prev.Orientation != cnf.Orientation { + w.config.Orientation = cnf.Orientation + w.orientation(cnf.Orientation) + } + if w.config != prev { + w.w.Event(ConfigEvent{Config: w.config}) + } +} + +func (w *window) Raise() {} + +func (w *window) SetCursor(name pointer.CursorName) { + style := w.cnv.Get("style") + style.Set("cursor", string(name)) +} + +func (w *window) Wakeup() { + select { + case w.wakeups <- struct{}{}: + default: + } +} + func (w *window) ShowTextInput(show bool) { // Run in a goroutine to avoid a deadlock if the // focus change result in an event. @@ -403,49 +559,119 @@ func (w *window) ShowTextInput(show bool) { }() } +func (w *window) SetInputHint(mode key.InputHint) { + w.keyboard(mode) +} + // Close the window. Not implemented for js. func (w *window) Close() {} +// Maximize the window. Not implemented for js. +func (w *window) Maximize() {} + +// Center the window. Not implemented for js. +func (w *window) Center() {} + +func (w *window) resize() { + w.scale = float32(w.window.Get("devicePixelRatio").Float()) + + rect := w.cnv.Call("getBoundingClientRect") + size := image.Point{ + X: int(float32(rect.Get("width").Float()) * w.scale), + Y: int(float32(rect.Get("height").Float()) * w.scale), + } + if size != w.config.Size { + w.config.Size = size + w.w.Event(ConfigEvent{Config: w.config}) + } + + if vx, vy := w.visualViewport.Get("width"), w.visualViewport.Get("height"); !vx.IsUndefined() && !vy.IsUndefined() { + w.inset.X = float32(w.config.Size.X) - float32(vx.Float())*w.scale + w.inset.Y = float32(w.config.Size.Y) - float32(vy.Float())*w.scale + } + + if w.config.Size.X == 0 || w.config.Size.Y == 0 { + return + } + + w.cnv.Set("width", w.config.Size.X) + w.cnv.Set("height", w.config.Size.Y) +} + func (w *window) draw(sync bool) { - width, height, scale, cfg := w.config() - if cfg == (unit.Metric{}) || width == 0 || height == 0 { + size, insets, metric := w.getConfig() + if metric == (unit.Metric{}) || size.X == 0 || size.Y == 0 { return } - w.mu.Lock() - w.scale = float32(scale) - w.mu.Unlock() - w.w.Event(FrameEvent{ + w.w.Event(frameEvent{ FrameEvent: system.FrameEvent{ - Now: time.Now(), - Size: image.Point{ - X: width, - Y: height, - }, - Metric: cfg, + Now: time.Now(), + Size: size, + Insets: insets, + Metric: metric, }, Sync: sync, }) } -func (w *window) config() (int, int, float32, unit.Metric) { - rect := w.cnv.Call("getBoundingClientRect") - width, height := rect.Get("width").Float(), rect.Get("height").Float() - scale := w.window.Get("devicePixelRatio").Float() - width *= scale - height *= scale - iw, ih := int(width+.5), int(height+.5) - // Adjust internal size of canvas if necessary. - if cw, ch := w.cnv.Get("width").Int(), w.cnv.Get("height").Int(); iw != cw || ih != ch { - w.cnv.Set("width", iw) - w.cnv.Set("height", ih) +func (w *window) getConfig() (image.Point, system.Insets, unit.Metric) { + return image.Pt(w.config.Size.X, w.config.Size.Y), system.Insets{ + Bottom: unit.Px(w.inset.Y), + Right: unit.Px(w.inset.X), + }, unit.Metric{ + PxPerDp: w.scale, + PxPerSp: w.scale, + } +} + +func (w *window) windowMode(mode WindowMode) { + switch mode { + case Windowed: + if !w.document.Get("fullscreenElement").Truthy() { + return // Browser is already Windowed. + } + if !w.document.Get("exitFullscreen").Truthy() { + return // Browser doesn't support such feature. + } + w.document.Call("exitFullscreen") + w.config.Mode = Windowed + case Fullscreen: + elem := w.document.Get("documentElement") + if !elem.Get("requestFullscreen").Truthy() { + return // Browser doesn't support such feature. + } + elem.Call("requestFullscreen") + w.config.Mode = Fullscreen + } +} + +func (w *window) orientation(mode Orientation) { + if j := w.screenOrientation; !j.Truthy() || !j.Get("unlock").Truthy() || !j.Get("lock").Truthy() { + return // Browser don't support Screen Orientation API. } - return iw, ih, float32(scale), unit.Metric{ - PxPerDp: float32(scale), - PxPerSp: float32(scale), + + switch mode { + case AnyOrientation: + w.screenOrientation.Call("unlock") + case LandscapeOrientation: + w.screenOrientation.Call("lock", "landscape").Call("then", w.redraw) + case PortraitOrientation: + w.screenOrientation.Call("lock", "portrait").Call("then", w.redraw) } } -func Main() { +func (w *window) navigationColor(c color.NRGBA) { + theme := w.head.Call("querySelector", `meta[name="theme-color"]`) + if !theme.Truthy() { + theme = w.document.Call("createElement", "meta") + theme.Set("name", "theme-color") + w.head.Call("appendChild", theme) + } + rgba := f32color.NRGBAToRGBA(c) + theme.Set("content", fmt.Sprintf("#%06X", []uint8{rgba.R, rgba.G, rgba.B})) +} + +func osMain() { select {} } @@ -479,7 +705,7 @@ func translateKey(k string) (string, bool) { case "Tab": n = key.NameTab case " ": - n = "Space" + n = key.NameSpace case "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12": n = k default: @@ -492,3 +718,5 @@ func translateKey(k string) (string, bool) { } return n, true } + +func (_ ViewEvent) ImplementsEvent() {} diff --git a/vendor/gioui.org/app/os_macos.go b/vendor/gioui.org/app/os_macos.go new file mode 100644 index 0000000..270db6a --- /dev/null +++ b/vendor/gioui.org/app/os_macos.go @@ -0,0 +1,663 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build darwin && !ios +// +build darwin,!ios + +package app + +import ( + "errors" + "image" + "runtime" + "time" + "unicode" + "unicode/utf16" + "unsafe" + + "gioui.org/f32" + "gioui.org/io/clipboard" + "gioui.org/io/key" + "gioui.org/io/pointer" + "gioui.org/io/system" + "gioui.org/unit" + + _ "gioui.org/internal/cocoainit" +) + +/* +#cgo CFLAGS: -DGL_SILENCE_DEPRECATION -Werror -Wno-deprecated-declarations -fmodules -fobjc-arc -x objective-c + +#include <AppKit/AppKit.h> + +#define MOUSE_MOVE 1 +#define MOUSE_UP 2 +#define MOUSE_DOWN 3 +#define MOUSE_SCROLL 4 + +__attribute__ ((visibility ("hidden"))) void gio_main(void); +__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createView(void); +__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight); + +static void writeClipboard(unichar *chars, NSUInteger length) { + @autoreleasepool { + NSString *s = [NSString string]; + if (length > 0) { + s = [NSString stringWithCharacters:chars length:length]; + } + NSPasteboard *p = NSPasteboard.generalPasteboard; + [p declareTypes:@[NSPasteboardTypeString] owner:nil]; + [p setString:s forType:NSPasteboardTypeString]; + } +} + +static CFTypeRef readClipboard(void) { + @autoreleasepool { + NSPasteboard *p = NSPasteboard.generalPasteboard; + NSString *content = [p stringForType:NSPasteboardTypeString]; + return (__bridge_retained CFTypeRef)content; + } +} + +static CGFloat viewHeight(CFTypeRef viewRef) { + NSView *view = (__bridge NSView *)viewRef; + return [view bounds].size.height; +} + +static CGFloat viewWidth(CFTypeRef viewRef) { + NSView *view = (__bridge NSView *)viewRef; + return [view bounds].size.width; +} + +static CGFloat getScreenBackingScale(void) { + return [NSScreen.mainScreen backingScaleFactor]; +} + +static CGFloat getViewBackingScale(CFTypeRef viewRef) { + NSView *view = (__bridge NSView *)viewRef; + return [view.window backingScaleFactor]; +} + +static void setNeedsDisplay(CFTypeRef viewRef) { + NSView *view = (__bridge NSView *)viewRef; + [view setNeedsDisplay:YES]; +} + +static NSPoint cascadeTopLeftFromPoint(CFTypeRef windowRef, NSPoint topLeft) { + NSWindow *window = (__bridge NSWindow *)windowRef; + return [window cascadeTopLeftFromPoint:topLeft]; +} + +static void makeKeyAndOrderFront(CFTypeRef windowRef) { + NSWindow *window = (__bridge NSWindow *)windowRef; + [window makeKeyAndOrderFront:nil]; +} + +static void toggleFullScreen(CFTypeRef windowRef) { + NSWindow *window = (__bridge NSWindow *)windowRef; + [window toggleFullScreen:nil]; +} + +static NSWindowStyleMask getWindowStyleMask(CFTypeRef windowRef) { + NSWindow *window = (__bridge NSWindow *)windowRef; + return [window styleMask]; +} + +static void closeWindow(CFTypeRef windowRef) { + NSWindow* window = (__bridge NSWindow *)windowRef; + [window performClose:nil]; +} + +static void setSize(CFTypeRef windowRef, CGFloat width, CGFloat height) { + NSWindow* window = (__bridge NSWindow *)windowRef; + NSSize size = NSMakeSize(width, height); + [window setContentSize:size]; +} + +static void setMinSize(CFTypeRef windowRef, CGFloat width, CGFloat height) { + NSWindow* window = (__bridge NSWindow *)windowRef; + window.contentMinSize = NSMakeSize(width, height); +} + +static void setMaxSize(CFTypeRef windowRef, CGFloat width, CGFloat height) { + NSWindow* window = (__bridge NSWindow *)windowRef; + window.contentMaxSize = NSMakeSize(width, height); +} + +static void setScreenFrame(CFTypeRef windowRef, CGFloat x, CGFloat y, CGFloat w, CGFloat h) { + NSWindow* window = (__bridge NSWindow *)windowRef; + NSRect r = NSMakeRect(x, y, w, h); + [window setFrame:r display:YES]; +} + +static NSRect getScreenFrame(CFTypeRef windowRef) { + NSWindow* window = (__bridge NSWindow *)windowRef; + return [[window screen] frame]; +} + +static void setTitle(CFTypeRef windowRef, const char *title) { + NSWindow* window = (__bridge NSWindow *)windowRef; + window.title = [NSString stringWithUTF8String: title]; +} + +static CFTypeRef layerForView(CFTypeRef viewRef) { + NSView *view = (__bridge NSView *)viewRef; + return (__bridge CFTypeRef)view.layer; +} + +static void raiseWindow(CFTypeRef windowRef) { + NSWindow* window = (__bridge NSWindow *)windowRef; + [window makeKeyAndOrderFront:nil]; +} + +*/ +import "C" + +func init() { + // Darwin requires that UI operations happen on the main thread only. + runtime.LockOSThread() +} + +// ViewEvent notified the client of changes to the window AppKit handles. +// The handles are retained until another ViewEvent is sent. +type ViewEvent struct { + // View is a CFTypeRef for the NSView for the window. + View uintptr + // Layer is a CFTypeRef of the CALayer of View. + Layer uintptr +} + +type window struct { + view C.CFTypeRef + window C.CFTypeRef + w *callbacks + stage system.Stage + displayLink *displayLink + cursor pointer.CursorName + + scale float32 + config Config +} + +// viewMap is the mapping from Cocoa NSViews to Go windows. +var viewMap = make(map[C.CFTypeRef]*window) + +// launched is closed when applicationDidFinishLaunching is called. +var launched = make(chan struct{}) + +// nextTopLeft is the offset to use for the next window's call to +// cascadeTopLeftFromPoint. +var nextTopLeft C.NSPoint + +// mustView is like lookupView, except that it panics +// if the view isn't mapped. +func mustView(view C.CFTypeRef) *window { + w, ok := lookupView(view) + if !ok { + panic("no window for view") + } + return w +} + +func lookupView(view C.CFTypeRef) (*window, bool) { + w, exists := viewMap[view] + if !exists { + return nil, false + } + return w, true +} + +func deleteView(view C.CFTypeRef) { + delete(viewMap, view) +} + +func insertView(view C.CFTypeRef, w *window) { + viewMap[view] = w +} + +func (w *window) contextView() C.CFTypeRef { + return w.view +} + +func (w *window) ReadClipboard() { + content := nsstringToString(C.readClipboard()) + w.w.Event(clipboard.Event{Text: content}) +} + +func (w *window) WriteClipboard(s string) { + u16 := utf16.Encode([]rune(s)) + var chars *C.unichar + if len(u16) > 0 { + chars = (*C.unichar)(unsafe.Pointer(&u16[0])) + } + C.writeClipboard(chars, C.NSUInteger(len(u16))) +} + +func (w *window) updateWindowMode() { + style := int(C.getWindowStyleMask(w.window)) + if style&C.NSWindowStyleMaskFullScreen > 0 { + w.config.Mode = Fullscreen + } else { + w.config.Mode = Windowed + } +} + +func (w *window) Configure(options []Option) { + screenScale := float32(C.getScreenBackingScale()) + cfg := configFor(screenScale) + prev := w.config + w.updateWindowMode() + cnf := w.config + cnf.apply(cfg, options) + cnf.Size = cnf.Size.Div(int(screenScale)) + cnf.MinSize = cnf.MinSize.Div(int(screenScale)) + cnf.MaxSize = cnf.MaxSize.Div(int(screenScale)) + + if cnf.Mode != Fullscreen && prev.Size != cnf.Size { + w.config.Size = cnf.Size + C.setSize(w.window, C.CGFloat(cnf.Size.X), C.CGFloat(cnf.Size.Y)) + } + if prev.MinSize != cnf.MinSize { + w.config.MinSize = cnf.MinSize + C.setMinSize(w.window, C.CGFloat(cnf.MinSize.X), C.CGFloat(cnf.MinSize.Y)) + } + if prev.MaxSize != cnf.MaxSize { + w.config.MaxSize = cnf.MaxSize + C.setMaxSize(w.window, C.CGFloat(cnf.MaxSize.X), C.CGFloat(cnf.MaxSize.Y)) + } + + if prev.Title != cnf.Title { + w.config.Title = cnf.Title + title := C.CString(cnf.Title) + defer C.free(unsafe.Pointer(title)) + C.setTitle(w.window, title) + } + if prev.Mode != cnf.Mode { + switch cnf.Mode { + case Windowed, Fullscreen: + w.config.Mode = cnf.Mode + C.toggleFullScreen(w.window) + } + } + if w.config != prev { + w.w.Event(ConfigEvent{Config: w.config}) + } +} + +func (w *window) SetCursor(name pointer.CursorName) { + w.cursor = windowSetCursor(w.cursor, name) +} + +func (w *window) ShowTextInput(show bool) {} + +func (w *window) SetInputHint(_ key.InputHint) {} + +func (w *window) SetAnimating(anim bool) { + if anim { + w.displayLink.Start() + } else { + w.displayLink.Stop() + } +} + +func (w *window) Raise() { + C.raiseWindow(w.window) +} + +func (w *window) runOnMain(f func()) { + runOnMain(func() { + // Make sure the view is still valid. The window might've been closed + // during the switch to the main thread. + if w.view != 0 { + f() + } + }) +} + +func (w *window) Close() { + C.closeWindow(w.window) +} + +// Maximize the window. +func (w *window) Maximize() { + r := C.getScreenFrame(w.window) // the screen size of the window + C.setScreenFrame(w.window, C.CGFloat(0), C.CGFloat(0), r.size.width, r.size.height) +} + +// Center the window. +func (w *window) Center() { + r := C.getScreenFrame(w.window) // the screen size of the window + + screenScale := float32(C.getScreenBackingScale()) + sz := w.config.Size.Div(int(screenScale)) + x := (int(r.size.width) - sz.X) / 2 + y := (int(r.size.height) - sz.Y) / 2 + + C.setScreenFrame(w.window, C.CGFloat(x), C.CGFloat(y), C.CGFloat(sz.X), C.CGFloat(sz.Y)) +} + +func (w *window) setStage(stage system.Stage) { + if stage == w.stage { + return + } + w.stage = stage + w.w.Event(system.StageEvent{Stage: stage}) +} + +//export gio_onKeys +func gio_onKeys(view C.CFTypeRef, cstr *C.char, ti C.double, mods C.NSUInteger, keyDown C.bool) { + str := C.GoString(cstr) + kmods := convertMods(mods) + ks := key.Release + if keyDown { + ks = key.Press + } + w := mustView(view) + for _, k := range str { + if n, ok := convertKey(k); ok { + w.w.Event(key.Event{ + Name: n, + Modifiers: kmods, + State: ks, + }) + } + } +} + +//export gio_onText +func gio_onText(view C.CFTypeRef, cstr *C.char) { + str := C.GoString(cstr) + w := mustView(view) + w.w.Event(key.EditEvent{Text: str}) +} + +//export gio_onMouse +func gio_onMouse(view C.CFTypeRef, cdir C.int, cbtns C.NSUInteger, x, y, dx, dy C.CGFloat, ti C.double, mods C.NSUInteger) { + var typ pointer.Type + switch cdir { + case C.MOUSE_MOVE: + typ = pointer.Move + case C.MOUSE_UP: + typ = pointer.Release + case C.MOUSE_DOWN: + typ = pointer.Press + case C.MOUSE_SCROLL: + typ = pointer.Scroll + default: + panic("invalid direction") + } + var btns pointer.Buttons + if cbtns&(1<<0) != 0 { + btns |= pointer.ButtonPrimary + } + if cbtns&(1<<1) != 0 { + btns |= pointer.ButtonSecondary + } + if cbtns&(1<<2) != 0 { + btns |= pointer.ButtonTertiary + } + t := time.Duration(float64(ti)*float64(time.Second) + .5) + w := mustView(view) + xf, yf := float32(x)*w.scale, float32(y)*w.scale + dxf, dyf := float32(dx)*w.scale, float32(dy)*w.scale + w.w.Event(pointer.Event{ + Type: typ, + Source: pointer.Mouse, + Time: t, + Buttons: btns, + Position: f32.Point{X: xf, Y: yf}, + Scroll: f32.Point{X: dxf, Y: dyf}, + Modifiers: convertMods(mods), + }) +} + +//export gio_onDraw +func gio_onDraw(view C.CFTypeRef) { + w := mustView(view) + w.draw() +} + +//export gio_onFocus +func gio_onFocus(view C.CFTypeRef, focus C.int) { + w := mustView(view) + w.w.Event(key.FocusEvent{Focus: focus == 1}) + w.SetCursor(w.cursor) +} + +//export gio_onChangeScreen +func gio_onChangeScreen(view C.CFTypeRef, did uint64) { + w := mustView(view) + w.displayLink.SetDisplayID(did) +} + +func (w *window) draw() { + w.scale = float32(C.getViewBackingScale(w.view)) + wf, hf := float32(C.viewWidth(w.view)), float32(C.viewHeight(w.view)) + sz := image.Point{ + X: int(wf*w.scale + .5), + Y: int(hf*w.scale + .5), + } + if sz != w.config.Size { + w.config.Size = sz + w.w.Event(ConfigEvent{Config: w.config}) + } + if sz.X == 0 || sz.Y == 0 { + return + } + cfg := configFor(w.scale) + w.setStage(system.StageRunning) + w.w.Event(frameEvent{ + FrameEvent: system.FrameEvent{ + Now: time.Now(), + Size: w.config.Size, + Metric: cfg, + }, + Sync: true, + }) +} + +func configFor(scale float32) unit.Metric { + return unit.Metric{ + PxPerDp: scale, + PxPerSp: scale, + } +} + +//export gio_onClose +func gio_onClose(view C.CFTypeRef) { + w := mustView(view) + w.w.Event(ViewEvent{}) + deleteView(view) + w.w.Event(system.DestroyEvent{}) + w.displayLink.Close() + C.CFRelease(w.view) + C.CFRelease(w.window) + w.view = 0 + w.window = 0 + w.displayLink = nil +} + +//export gio_onHide +func gio_onHide(view C.CFTypeRef) { + w := mustView(view) + w.setStage(system.StagePaused) +} + +//export gio_onShow +func gio_onShow(view C.CFTypeRef) { + w := mustView(view) + w.setStage(system.StageRunning) +} + +//export gio_onFullscreen +func gio_onFullscreen(view C.CFTypeRef) { + w := mustView(view) + w.config.Mode = Fullscreen + w.w.Event(ConfigEvent{Config: w.config}) +} + +//export gio_onWindowed +func gio_onWindowed(view C.CFTypeRef) { + w := mustView(view) + w.config.Mode = Windowed + w.w.Event(ConfigEvent{Config: w.config}) +} + +//export gio_onAppHide +func gio_onAppHide() { + for _, w := range viewMap { + w.setStage(system.StagePaused) + } +} + +//export gio_onAppShow +func gio_onAppShow() { + for _, w := range viewMap { + w.setStage(system.StageRunning) + } +} + +//export gio_onFinishLaunching +func gio_onFinishLaunching() { + close(launched) +} + +func newWindow(win *callbacks, options []Option) error { + <-launched + errch := make(chan error) + runOnMain(func() { + w, err := newOSWindow() + if err != nil { + errch <- err + return + } + errch <- nil + w.w = win + w.window = C.gio_createWindow(w.view, nil, 0, 0, 0, 0, 0, 0) + win.SetDriver(w) + w.Configure(options) + if nextTopLeft.x == 0 && nextTopLeft.y == 0 { + // cascadeTopLeftFromPoint treats (0, 0) as a no-op, + // and just returns the offset we need for the first window. + nextTopLeft = C.cascadeTopLeftFromPoint(w.window, nextTopLeft) + } + nextTopLeft = C.cascadeTopLeftFromPoint(w.window, nextTopLeft) + C.makeKeyAndOrderFront(w.window) + layer := C.layerForView(w.view) + w.w.Event(ViewEvent{View: uintptr(w.view), Layer: uintptr(layer)}) + }) + return <-errch +} + +func newOSWindow() (*window, error) { + view := C.gio_createView() + if view == 0 { + return nil, errors.New("CreateWindow: failed to create view") + } + scale := float32(C.getViewBackingScale(view)) + w := &window{ + view: view, + scale: scale, + } + dl, err := NewDisplayLink(func() { + w.runOnMain(func() { + C.setNeedsDisplay(w.view) + }) + }) + w.displayLink = dl + if err != nil { + C.CFRelease(view) + return nil, err + } + insertView(view, w) + return w, nil +} + +func osMain() { + C.gio_main() +} + +func convertKey(k rune) (string, bool) { + var n string + switch k { + case 0x1b: + n = key.NameEscape + case C.NSLeftArrowFunctionKey: + n = key.NameLeftArrow + case C.NSRightArrowFunctionKey: + n = key.NameRightArrow + case C.NSUpArrowFunctionKey: + n = key.NameUpArrow + case C.NSDownArrowFunctionKey: + n = key.NameDownArrow + case 0xd: + n = key.NameReturn + case 0x3: + n = key.NameEnter + case C.NSHomeFunctionKey: + n = key.NameHome + case C.NSEndFunctionKey: + n = key.NameEnd + case 0x7f: + n = key.NameDeleteBackward + case C.NSDeleteFunctionKey: + n = key.NameDeleteForward + case C.NSPageUpFunctionKey: + n = key.NamePageUp + case C.NSPageDownFunctionKey: + n = key.NamePageDown + case C.NSF1FunctionKey: + n = "F1" + case C.NSF2FunctionKey: + n = "F2" + case C.NSF3FunctionKey: + n = "F3" + case C.NSF4FunctionKey: + n = "F4" + case C.NSF5FunctionKey: + n = "F5" + case C.NSF6FunctionKey: + n = "F6" + case C.NSF7FunctionKey: + n = "F7" + case C.NSF8FunctionKey: + n = "F8" + case C.NSF9FunctionKey: + n = "F9" + case C.NSF10FunctionKey: + n = "F10" + case C.NSF11FunctionKey: + n = "F11" + case C.NSF12FunctionKey: + n = "F12" + case 0x09, 0x19: + n = key.NameTab + case 0x20: + n = key.NameSpace + default: + k = unicode.ToUpper(k) + if !unicode.IsPrint(k) { + return "", false + } + n = string(k) + } + return n, true +} + +func convertMods(mods C.NSUInteger) key.Modifiers { + var kmods key.Modifiers + if mods&C.NSAlternateKeyMask != 0 { + kmods |= key.ModAlt + } + if mods&C.NSControlKeyMask != 0 { + kmods |= key.ModCtrl + } + if mods&C.NSCommandKeyMask != 0 { + kmods |= key.ModCommand + } + if mods&C.NSShiftKeyMask != 0 { + kmods |= key.ModShift + } + return kmods +} + +func (_ ViewEvent) ImplementsEvent() {} diff --git a/vendor/gioui.org/app/internal/window/os_macos.m b/vendor/gioui.org/app/os_macos.m index b8c0dee..f65099b 100644 --- a/vendor/gioui.org/app/internal/window/os_macos.m +++ b/vendor/gioui.org/app/os_macos.m @@ -6,6 +6,8 @@ #include "_cgo_export.h" +__attribute__ ((visibility ("hidden"))) CALayer *gio_layerFactory(void); + @interface GioAppDelegate : NSObject<NSApplicationDelegate> @end @@ -21,6 +23,14 @@ NSWindow *window = (NSWindow *)[notification object]; gio_onShow((__bridge CFTypeRef)window.contentView); } +- (void)windowWillEnterFullScreen:(NSNotification *)notification { + NSWindow *window = (NSWindow *)[notification object]; + gio_onFullscreen((__bridge CFTypeRef)window.contentView); +} +- (void)windowWillExitFullScreen:(NSNotification *)notification { + NSWindow *window = (NSWindow *)[notification object]; + gio_onWindowed((__bridge CFTypeRef)window.contentView); +} - (void)windowDidChangeScreen:(NSNotification *)notification { NSWindow *window = (NSWindow *)[notification object]; CGDirectDisplayID dispID = [[[window screen] deviceDescription][@"NSScreenNumber"] unsignedIntValue]; @@ -29,11 +39,11 @@ } - (void)windowDidBecomeKey:(NSNotification *)notification { NSWindow *window = (NSWindow *)[notification object]; - gio_onFocus((__bridge CFTypeRef)window.contentView, YES); + gio_onFocus((__bridge CFTypeRef)window.contentView, 1); } - (void)windowDidResignKey:(NSNotification *)notification { NSWindow *window = (NSWindow *)[notification object]; - gio_onFocus((__bridge CFTypeRef)window.contentView, NO); + gio_onFocus((__bridge CFTypeRef)window.contentView, 0); } - (void)windowWillClose:(NSNotification *)notification { NSWindow *window = (NSWindow *)[notification object]; @@ -42,49 +52,92 @@ } @end -// Delegates are weakly referenced from their peers. Nothing -// else holds a strong reference to our window delegate, so -// keep a single global reference instead. -static GioWindowDelegate *globalWindowDel; - -void gio_writeClipboard(unichar *chars, NSUInteger length) { - @autoreleasepool { - NSString *s = [NSString string]; - if (length > 0) { - s = [NSString stringWithCharacters:chars length:length]; - } - NSPasteboard *p = NSPasteboard.generalPasteboard; - [p declareTypes:@[NSPasteboardTypeString] owner:nil]; - [p setString:s forType:NSPasteboardTypeString]; +static void handleMouse(NSView *view, NSEvent *event, int typ, CGFloat dx, CGFloat dy) { + NSPoint p = [view convertPoint:[event locationInWindow] fromView:nil]; + if (!event.hasPreciseScrollingDeltas) { + // dx and dy are in rows and columns. + dx *= 10; + dy *= 10; } + // Origin is in the lower left corner. Convert to upper left. + CGFloat height = view.bounds.size.height; + gio_onMouse((__bridge CFTypeRef)view, typ, [NSEvent pressedMouseButtons], p.x, height - p.y, dx, dy, [event timestamp], [event modifierFlags]); } -CFTypeRef gio_readClipboard(void) { - @autoreleasepool { - NSPasteboard *p = NSPasteboard.generalPasteboard; - NSString *content = [p stringForType:NSPasteboardTypeString]; - return (__bridge_retained CFTypeRef)content; - } -} +@interface GioView : NSView <CALayerDelegate> +@end -CGFloat gio_viewHeight(CFTypeRef viewRef) { - NSView *view = (__bridge NSView *)viewRef; - return [view bounds].size.height; +@implementation GioView +- (void)setFrameSize:(NSSize)newSize { + [super setFrameSize:newSize]; + [self setNeedsDisplay:YES]; } - -CGFloat gio_viewWidth(CFTypeRef viewRef) { - NSView *view = (__bridge NSView *)viewRef; - return [view bounds].size.width; +// drawRect is called when OpenGL is used, displayLayer otherwise. +// Don't know why. +- (void)drawRect:(NSRect)r { + gio_onDraw((__bridge CFTypeRef)self); } - -CGFloat gio_getScreenBackingScale(void) { - return [NSScreen.mainScreen backingScaleFactor]; +- (void)displayLayer:(CALayer *)layer { + layer.contentsScale = self.window.backingScaleFactor; + gio_onDraw((__bridge CFTypeRef)self); } - -CGFloat gio_getViewBackingScale(CFTypeRef viewRef) { - NSView *view = (__bridge NSView *)viewRef; - return [view.window backingScaleFactor]; +- (CALayer *)makeBackingLayer { + CALayer *layer = gio_layerFactory(); + layer.delegate = self; + return layer; +} +- (void)mouseDown:(NSEvent *)event { + handleMouse(self, event, MOUSE_DOWN, 0, 0); +} +- (void)mouseUp:(NSEvent *)event { + handleMouse(self, event, MOUSE_UP, 0, 0); +} +- (void)middleMouseDown:(NSEvent *)event { + handleMouse(self, event, MOUSE_DOWN, 0, 0); +} +- (void)middletMouseUp:(NSEvent *)event { + handleMouse(self, event, MOUSE_UP, 0, 0); +} +- (void)rightMouseDown:(NSEvent *)event { + handleMouse(self, event, MOUSE_DOWN, 0, 0); +} +- (void)rightMouseUp:(NSEvent *)event { + handleMouse(self, event, MOUSE_UP, 0, 0); } +- (void)mouseMoved:(NSEvent *)event { + handleMouse(self, event, MOUSE_MOVE, 0, 0); +} +- (void)mouseDragged:(NSEvent *)event { + handleMouse(self, event, MOUSE_MOVE, 0, 0); +} +- (void)scrollWheel:(NSEvent *)event { + CGFloat dx = -event.scrollingDeltaX; + CGFloat dy = -event.scrollingDeltaY; + handleMouse(self, event, MOUSE_SCROLL, dx, dy); +} +- (void)keyDown:(NSEvent *)event { + NSString *keys = [event charactersIgnoringModifiers]; + gio_onKeys((__bridge CFTypeRef)self, (char *)[keys UTF8String], [event timestamp], [event modifierFlags], true); + [self interpretKeyEvents:[NSArray arrayWithObject:event]]; +} +- (void)keyUp:(NSEvent *)event { + NSString *keys = [event charactersIgnoringModifiers]; + gio_onKeys((__bridge CFTypeRef)self, (char *)[keys UTF8String], [event timestamp], [event modifierFlags], false); +} +- (void)insertText:(id)string { + const char *utf8 = [string UTF8String]; + gio_onText((__bridge CFTypeRef)self, (char *)utf8); +} +- (void)doCommandBySelector:(SEL)sel { + // Don't pass commands up the responder chain. + // They will end up in a beep. +} +@end + +// Delegates are weakly referenced from their peers. Nothing +// else holds a strong reference to our window delegate, so +// keep a single global reference instead. +static GioWindowDelegate *globalWindowDel; static CVReturn displayLinkCallback(CVDisplayLinkRef dl, const CVTimeStamp *inNow, const CVTimeStamp *inOutputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext) { gio_onFrameCallback(dl); @@ -114,14 +167,47 @@ void gio_setDisplayLinkDisplay(CFTypeRef dl, uint64_t did) { CVDisplayLinkSetCurrentCGDisplay((CVDisplayLinkRef)dl, (CGDirectDisplayID)did); } -NSPoint gio_cascadeTopLeftFromPoint(CFTypeRef windowRef, NSPoint topLeft) { - NSWindow *window = (__bridge NSWindow *)windowRef; - return [window cascadeTopLeftFromPoint:topLeft]; +void gio_hideCursor() { + @autoreleasepool { + [NSCursor hide]; + } +} + +void gio_showCursor() { + @autoreleasepool { + [NSCursor unhide]; + } } -void gio_makeKeyAndOrderFront(CFTypeRef windowRef) { - NSWindow *window = (__bridge NSWindow *)windowRef; - [window makeKeyAndOrderFront:nil]; +void gio_setCursor(NSUInteger curID) { + @autoreleasepool { + switch (curID) { + case 1: + [NSCursor.arrowCursor set]; + break; + case 2: + [NSCursor.IBeamCursor set]; + break; + case 3: + [NSCursor.pointingHandCursor set]; + break; + case 4: + [NSCursor.crosshairCursor set]; + break; + case 5: + [NSCursor.resizeLeftRightCursor set]; + break; + case 6: + [NSCursor.resizeUpDownCursor set]; + break; + case 7: + [NSCursor.openHandCursor set]; + break; + default: + [NSCursor.arrowCursor set]; + break; + } + } } CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight) { @@ -143,7 +229,9 @@ CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, window.contentMaxSize = NSMakeSize(maxWidth, maxHeight); } [window setAcceptsMouseMovedEvents:YES]; - window.title = [NSString stringWithUTF8String: title]; + if (title != nil) { + window.title = [NSString stringWithUTF8String: title]; + } NSView *view = (__bridge NSView *)viewRef; [window setContentView:view]; [window makeFirstResponder:view]; @@ -153,14 +241,20 @@ CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, } } -void gio_close(CFTypeRef windowRef) { - NSWindow* window = (__bridge NSWindow *)windowRef; - [window performClose:nil]; +CFTypeRef gio_createView(void) { + @autoreleasepool { + NSRect frame = NSMakeRect(0, 0, 0, 0); + GioView* view = [[GioView alloc] initWithFrame:frame]; + view.wantsLayer = YES; + view.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize; + return CFBridgingRetain(view); + } } @implementation GioAppDelegate - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { - [[NSRunningApplication currentApplication] activateWithOptions:(NSApplicationActivateAllWindows | NSApplicationActivateIgnoringOtherApps)]; + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + [NSApp activateIgnoringOtherApps:YES]; gio_onFinishLaunching(); } - (void)applicationDidHide:(NSNotification *)aNotification { @@ -176,7 +270,6 @@ void gio_main() { [NSApplication sharedApplication]; GioAppDelegate *del = [[GioAppDelegate alloc] init]; [NSApp setDelegate:del]; - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; NSMenuItem *mainMenu = [NSMenuItem new]; diff --git a/vendor/gioui.org/app/os_unix.go b/vendor/gioui.org/app/os_unix.go new file mode 100644 index 0000000..ee831b3 --- /dev/null +++ b/vendor/gioui.org/app/os_unix.go @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build (linux && !android) || freebsd || openbsd +// +build linux,!android freebsd openbsd + +package app + +import ( + "errors" + "unsafe" +) + +type ViewEvent struct { + // Display is a pointer to the X11 Display created by XOpenDisplay. + Display unsafe.Pointer + // Window is the X11 window ID as returned by XCreateWindow. + Window uintptr +} + +func osMain() { + select {} +} + +type windowDriver func(*callbacks, []Option) error + +// Instead of creating files with build tags for each combination of wayland +/- x11 +// let each driver initialize these variables with their own version of createWindow. +var wlDriver, x11Driver windowDriver + +func newWindow(window *callbacks, options []Option) error { + var errFirst error + for _, d := range []windowDriver{x11Driver, wlDriver} { + if d == nil { + continue + } + err := d(window, options) + if err == nil { + return nil + } + if errFirst == nil { + errFirst = err + } + } + if errFirst != nil { + return errFirst + } + return errors.New("app: no window driver available") +} + +func (_ ViewEvent) ImplementsEvent() {} diff --git a/vendor/gioui.org/app/internal/window/os_wayland.c b/vendor/gioui.org/app/os_wayland.c index 5c1c075..b137e1f 100644 --- a/vendor/gioui.org/app/internal/window/os_wayland.c +++ b/vendor/gioui.org/app/os_wayland.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: Unlicense OR MIT -// +build linux,!android,!nowayland freebsd +//go:build ((linux && !android) || freebsd) && !nowayland +// +build linux,!android freebsd +// +build !nowayland #include <wayland-client.h> #include "wayland_xdg_shell.h" diff --git a/vendor/gioui.org/app/internal/window/os_wayland.go b/vendor/gioui.org/app/os_wayland.go index 4a456ce..ac7aa17 100644 --- a/vendor/gioui.org/app/internal/window/os_wayland.go +++ b/vendor/gioui.org/app/os_wayland.go @@ -1,8 +1,10 @@ // SPDX-License-Identifier: Unlicense OR MIT -// +build linux,!android,!nowayland freebsd +//go:build ((linux && !android) || freebsd) && !nowayland +// +build linux,!android freebsd +// +build !nowayland -package window +package app import ( "bytes" @@ -19,14 +21,16 @@ import ( "time" "unsafe" + syscall "golang.org/x/sys/unix" + "gioui.org/app/internal/xkb" "gioui.org/f32" "gioui.org/internal/fling" + "gioui.org/io/clipboard" "gioui.org/io/key" "gioui.org/io/pointer" "gioui.org/io/system" "gioui.org/unit" - syscall "golang.org/x/sys/unix" ) // Use wayland-scanner to generate glue code for the xdg-shell and xdg-decoration extensions. @@ -39,9 +43,9 @@ import ( //go:generate wayland-scanner client-header /usr/share/wayland-protocols/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml wayland_xdg_decoration.h //go:generate wayland-scanner private-code /usr/share/wayland-protocols/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml wayland_xdg_decoration.c -//go:generate sed -i "1s;^;// +build linux,!android,!nowayland freebsd\\n\\n;" wayland_xdg_shell.c -//go:generate sed -i "1s;^;// +build linux,!android,!nowayland freebsd\\n\\n;" wayland_xdg_decoration.c -//go:generate sed -i "1s;^;// +build linux,!android,!nowayland freebsd\\n\\n;" wayland_text_input.c +//go:generate sed -i "1s;^;//go:build ((linux \\&\\& !android) || freebsd) \\&\\& !nowayland\\n// +build linux,!android freebsd\\n// +build !nowayland\\n\\n;" wayland_xdg_shell.c +//go:generate sed -i "1s;^;//go:build ((linux \\&\\& !android) || freebsd) \\&\\& !nowayland\\n// +build linux,!android freebsd\\n// +build !nowayland\\n\\n;" wayland_xdg_decoration.c +//go:generate sed -i "1s;^;//go:build ((linux \\&\\& !android) || freebsd) \\&\\& !nowayland\\n// +build linux,!android freebsd\\n// +build !nowayland\\n\\n;" wayland_text_input.c /* #cgo linux pkg-config: wayland-client wayland-cursor @@ -133,7 +137,7 @@ type repeatState struct { delay time.Duration key uint32 - win Callbacks + win *callbacks stopC chan struct{} start time.Duration @@ -143,7 +147,7 @@ type repeatState struct { } type window struct { - w Callbacks + w *callbacks disp *wlDisplay surf *C.struct_wl_surface wmSurf *C.struct_xdg_surface @@ -177,19 +181,17 @@ type window struct { dead bool lastFrameCallback *C.struct_wl_callback - mu sync.Mutex animating bool needAck bool // The most recent configure serial waiting to be ack'ed. serial C.uint32_t - width int - height int newScale bool scale int - // readClipboard tracks whether a ClipboardEvent is requested. - readClipboard bool - // writeClipboard is set whenever a clipboard write is requested. - writeClipboard *string + // size is the unscaled window size (unlike config.Size which is scaled). + size image.Point + config Config + + wakeups chan struct{} } type poller struct { @@ -218,25 +220,34 @@ var callbackMap sync.Map // order of preference. var clipboardMimeTypes = []string{"text/plain;charset=utf8", "UTF8_STRING", "text/plain", "TEXT", "STRING"} +var ( + newWaylandEGLContext func(w *window) (context, error) + newWaylandVulkanContext func(w *window) (context, error) +) + func init() { wlDriver = newWLWindow } -func newWLWindow(window Callbacks, opts *Options) error { +func newWLWindow(callbacks *callbacks, options []Option) error { d, err := newWLDisplay() if err != nil { return err } - w, err := d.createNativeWindow(opts) + w, err := d.createNativeWindow(options) if err != nil { d.destroy() return err } - w.w = window + w.w = callbacks go func() { defer d.destroy() defer w.destroy() + w.w.SetDriver(w) + // Finish and commit setup from createNativeWindow. + w.Configure(options) + C.wl_surface_commit(w.surf) if err := w.loop(); err != nil { panic(err) } @@ -280,13 +291,16 @@ func (d *wlDisplay) readClipboard() (io.ReadCloser, error) { if err != nil { return nil, err } + // wl_data_offer_receive performs and implicit dup(2) of the write end + // of the pipe. Close our version. + defer w.Close() cmimeType := C.CString(s.mimeType) defer C.free(unsafe.Pointer(cmimeType)) C.wl_data_offer_receive(s.clipboard, cmimeType, C.int(w.Fd())) return r, nil } -func (d *wlDisplay) createNativeWindow(opts *Options) (*window, error) { +func (d *wlDisplay) createNativeWindow(options []Option) (*window, error) { if d.compositor == nil { return nil, errors.New("wayland: no compositor available") } @@ -313,6 +327,7 @@ func (d *wlDisplay) createNativeWindow(opts *Options) (*window, error) { newScale: scale != 1, ppdp: ppdp, ppsp: ppdp, + wakeups: make(chan struct{}, 1), } w.surf = C.wl_compositor_create_surface(d.compositor) if w.surf == nil { @@ -351,20 +366,13 @@ func (d *wlDisplay) createNativeWindow(opts *Options) (*window, error) { C.wl_surface_add_listener(w.surf, &C.gio_surface_listener, unsafe.Pointer(w.surf)) C.xdg_surface_add_listener(w.wmSurf, &C.gio_xdg_surface_listener, unsafe.Pointer(w.surf)) C.xdg_toplevel_add_listener(w.topLvl, &C.gio_xdg_toplevel_listener, unsafe.Pointer(w.surf)) - title := C.CString(opts.Title) - C.xdg_toplevel_set_title(w.topLvl, title) - C.free(unsafe.Pointer(title)) - _, _, cfg := w.config() - w.width = cfg.Px(opts.Width) - w.height = cfg.Px(opts.Height) if d.decor != nil { // Request server side decorations. w.decor = C.zxdg_decoration_manager_v1_get_toplevel_decoration(d.decor, w.topLvl) C.zxdg_toplevel_decoration_v1_set_mode(w.decor, C.ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE) } w.updateOpaqueRegion() - C.wl_surface_commit(w.surf) return w, nil } @@ -471,10 +479,8 @@ func gio_onSeatName(data unsafe.Pointer, seat *C.struct_wl_seat, name *C.char) { //export gio_onXdgSurfaceConfigure func gio_onXdgSurfaceConfigure(data unsafe.Pointer, wmSurf *C.struct_xdg_surface, serial C.uint32_t) { w := callbackLoad(data).(*window) - w.mu.Lock() w.serial = serial w.needAck = true - w.mu.Unlock() w.setStage(system.StageRunning) w.draw(true) } @@ -489,10 +495,7 @@ func gio_onToplevelClose(data unsafe.Pointer, topLvl *C.struct_xdg_toplevel) { func gio_onToplevelConfigure(data unsafe.Pointer, topLvl *C.struct_xdg_toplevel, width, height C.int32_t, states *C.struct_wl_array) { w := callbackLoad(data).(*window) if width != 0 && height != 0 { - w.mu.Lock() - defer w.mu.Unlock() - w.width = int(width) - w.height = int(height) + w.size = image.Pt(int(width), int(height)) w.updateOpaqueRegion() } } @@ -584,10 +587,11 @@ func gio_onRegistryGlobal(data unsafe.Pointer, reg *C.struct_wl_registry, name C break } d.seat = &wlSeat{ - disp: d, - name: name, - seat: s, - offers: make(map[*C.struct_wl_data_offer][]string), + disp: d, + name: name, + seat: s, + offers: make(map[*C.struct_wl_data_offer][]string), + touchFoci: make(map[C.int32_t]*window), } callbackStore(unsafe.Pointer(s), d.seat) C.wl_seat_add_listener(s, &C.gio_seat_listener, unsafe.Pointer(s)) @@ -763,16 +767,7 @@ func gio_onPointerEnter(data unsafe.Pointer, pointer *C.struct_wl_pointer, seria s.serial = serial w := callbackLoad(unsafe.Pointer(surf)).(*window) s.pointerFocus = w - // Get images[0]. - img := *w.cursor.cursor.images - buf := C.wl_cursor_image_get_buffer(img) - if buf == nil { - return - } - C.wl_pointer_set_cursor(pointer, serial, w.cursor.surf, C.int32_t(img.hotspot_x), C.int32_t(img.hotspot_y)) - C.wl_surface_attach(w.cursor.surf, buf, 0, 0) - C.wl_surface_damage(w.cursor.surf, 0, 0, C.int32_t(img.width), C.int32_t(img.height)) - C.wl_surface_commit(w.cursor.surf) + w.setCursor(pointer, serial) w.lastPos = f32.Point{X: fromFixed(x), Y: fromFixed(y)} } @@ -804,11 +799,11 @@ func gio_onPointerButton(data unsafe.Pointer, p *C.struct_wl_pointer, serial, t, var btn pointer.Buttons switch wbtn { case BTN_LEFT: - btn = pointer.ButtonLeft + btn = pointer.ButtonPrimary case BTN_RIGHT: - btn = pointer.ButtonRight + btn = pointer.ButtonSecondary case BTN_MIDDLE: - btn = pointer.ButtonMiddle + btn = pointer.ButtonTertiary default: return } @@ -867,15 +862,13 @@ func (w *window) flushFling() { w.fling.xExtrapolation = fling.Extrapolation{} w.fling.yExtrapolation = fling.Extrapolation{} vel := float32(math.Sqrt(float64(estx.Velocity*estx.Velocity + esty.Velocity*esty.Velocity))) - _, _, c := w.config() + _, c := w.getConfig() if !w.fling.anim.Start(c, time.Now(), vel) { return } invDist := 1 / vel w.fling.dir.X = estx.Velocity * invDist w.fling.dir.Y = esty.Velocity * invDist - // Wake up the window loop. - w.disp.wakeup() } //export gio_onPointerAxisSource @@ -903,17 +896,94 @@ func gio_onPointerAxisDiscrete(data unsafe.Pointer, p *C.struct_wl_pointer, axis } func (w *window) ReadClipboard() { - w.mu.Lock() - w.readClipboard = true - w.mu.Unlock() - w.disp.wakeup() + r, err := w.disp.readClipboard() + // Send empty responses on unavailable clipboards or errors. + if r == nil || err != nil { + w.w.Event(clipboard.Event{}) + return + } + // Don't let slow clipboard transfers block event loop. + go func() { + defer r.Close() + data, _ := ioutil.ReadAll(r) + w.w.Event(clipboard.Event{Text: string(data)}) + }() } func (w *window) WriteClipboard(s string) { - w.mu.Lock() - w.writeClipboard = &s - w.mu.Unlock() - w.disp.wakeup() + w.disp.writeClipboard([]byte(s)) +} + +func (w *window) Configure(options []Option) { + _, cfg := w.getConfig() + prev := w.config + cnf := w.config + cnf.apply(cfg, options) + if prev.Size != cnf.Size { + w.size = image.Pt(cnf.Size.X/w.scale, cnf.Size.Y/w.scale) + w.config.Size = cnf.Size + } + if prev.Title != cnf.Title { + w.config.Title = cnf.Title + title := C.CString(cnf.Title) + C.xdg_toplevel_set_title(w.topLvl, title) + C.free(unsafe.Pointer(title)) + } + if w.config != prev { + w.w.Event(ConfigEvent{Config: w.config}) + } +} + +func (w *window) Raise() {} + +func (w *window) SetCursor(name pointer.CursorName) { + ptr := w.disp.seat.pointer + if ptr == nil { + return + } + if name == pointer.CursorNone { + C.wl_pointer_set_cursor(ptr, w.serial, nil, 0, 0) + return + } + switch name { + default: + fallthrough + case pointer.CursorDefault: + name = "left_ptr" + case pointer.CursorText: + name = "xterm" + case pointer.CursorPointer: + name = "hand1" + case pointer.CursorCrossHair: + name = "crosshair" + case pointer.CursorRowResize: + name = "top_side" + case pointer.CursorColResize: + name = "left_side" + case pointer.CursorGrab: + name = "hand1" + } + cname := C.CString(string(name)) + defer C.free(unsafe.Pointer(cname)) + c := C.wl_cursor_theme_get_cursor(w.cursor.theme, cname) + if c == nil { + return + } + w.cursor.cursor = c + w.setCursor(ptr, w.serial) +} + +func (w *window) setCursor(pointer *C.struct_wl_pointer, serial C.uint32_t) { + // Get images[0]. + img := *w.cursor.cursor.images + buf := C.wl_cursor_image_get_buffer(img) + if buf == nil { + return + } + C.wl_pointer_set_cursor(pointer, serial, w.cursor.surf, C.int32_t(img.hotspot_x), C.int32_t(img.hotspot_y)) + C.wl_surface_attach(w.cursor.surf, buf, 0, 0) + C.wl_surface_damage(w.cursor.surf, 0, 0, C.int32_t(img.width), C.int32_t(img.height)) + C.wl_surface_commit(w.cursor.surf) } func (w *window) resetFling() { @@ -963,13 +1033,14 @@ func gio_onKeyboardKey(data unsafe.Pointer, keyboard *C.struct_wl_keyboard, seri t := time.Duration(timestamp) * time.Millisecond s.disp.repeat.Stop(t) w.resetFling() - if state != C.WL_KEYBOARD_KEY_STATE_PRESSED { - return - } kc := mapXKBKeycode(uint32(keyCode)) - for _, e := range w.disp.xkb.DispatchKey(kc) { + ks := mapXKBKeyState(uint32(state)) + for _, e := range w.disp.xkb.DispatchKey(kc, ks) { w.w.Event(e) } + if state != C.WL_KEYBOARD_KEY_STATE_PRESSED { + return + } if w.disp.xkb.IsRepeatKey(kc) { w.disp.repeat.Start(w, kc, t) } @@ -980,6 +1051,15 @@ func mapXKBKeycode(keyCode uint32) uint32 { return keyCode + 8 } +func mapXKBKeyState(state uint32) key.State { + switch state { + case C.WL_KEYBOARD_KEY_STATE_RELEASED: + return key.Release + default: + return key.Press + } +} + func (r *repeatState) Start(w *window, keyCode uint32, t time.Duration) { if r.rate <= 0 { return @@ -1045,7 +1125,7 @@ func (r *repeatState) Repeat(d *wlDisplay) { if r.last+delay > now { break } - for _, e := range d.xkb.DispatchKey(r.key) { + for _, e := range d.xkb.DispatchKey(r.key, key.Press) { r.win.Event(e) } r.last += delay @@ -1068,43 +1148,21 @@ func (w *window) loop() error { if err := w.disp.dispatch(&p); err != nil { return err } + select { + case <-w.wakeups: + w.w.Event(wakeupEvent{}) + default: + } if w.dead { w.w.Event(system.DestroyEvent{}) break } - w.process() + // pass false to skip unnecessary drawing. + w.draw(false) } return nil } -func (w *window) process() { - w.mu.Lock() - readClipboard := w.readClipboard - writeClipboard := w.writeClipboard - w.readClipboard = false - w.writeClipboard = nil - w.mu.Unlock() - if readClipboard { - r, err := w.disp.readClipboard() - // Send empty responses on unavailable clipboards or errors. - if r == nil || err != nil { - w.w.Event(system.ClipboardEvent{}) - return - } - // Don't let slow clipboard transfers block event loop. - go func() { - defer r.Close() - data, _ := ioutil.ReadAll(r) - w.w.Event(system.ClipboardEvent{Text: string(data)}) - }() - } - if writeClipboard != nil { - w.disp.writeClipboard([]byte(*writeClipboard)) - } - // pass false to skip unnecessary drawing. - w.draw(false) -} - func (d *wlDisplay) dispatch(p *poller) error { dispfd := C.wl_display_get_fd(d.disp) // Poll for events and notifications. @@ -1147,11 +1205,16 @@ func (d *wlDisplay) dispatch(p *poller) error { return nil } +func (w *window) Wakeup() { + select { + case w.wakeups <- struct{}{}: + default: + } + w.disp.wakeup() +} + func (w *window) SetAnimating(anim bool) { - w.mu.Lock() w.animating = anim - w.mu.Unlock() - w.disp.wakeup() } // Wakeup wakes up the event loop through the notification pipe. @@ -1322,7 +1385,7 @@ func (w *window) onPointerMotion(x, y C.wl_fixed_t, t C.uint32_t) { func (w *window) updateOpaqueRegion() { reg := C.wl_compositor_create_region(w.disp.compositor) - C.wl_region_add(reg, 0, 0, C.int32_t(w.width), C.int32_t(w.height)) + C.wl_region_add(reg, 0, 0, C.int32_t(w.size.X), C.int32_t(w.size.Y)) C.wl_surface_set_opaque_region(w.surf, reg) C.wl_region_destroy(reg) } @@ -1340,12 +1403,10 @@ func (w *window) updateOutputs() { } } } - w.mu.Lock() if found && scale != w.scale { w.scale = scale w.newScale = true } - w.mu.Unlock() if !found { w.setStage(system.StagePaused) } else { @@ -1354,9 +1415,9 @@ func (w *window) updateOutputs() { } } -func (w *window) config() (int, int, unit.Metric) { - width, height := w.width*w.scale, w.height*w.scale - return width, height, unit.Metric{ +func (w *window) getConfig() (image.Point, unit.Metric) { + size := w.size.Mul(w.scale) + return size, unit.Metric{ PxPerDp: w.ppdp * float32(w.scale), PxPerSp: w.ppsp * float32(w.scale), } @@ -1364,14 +1425,16 @@ func (w *window) config() (int, int, unit.Metric) { func (w *window) draw(sync bool) { w.flushScroll() - w.mu.Lock() anim := w.animating || w.fling.anim.Active() dead := w.dead - w.mu.Unlock() if dead || (!anim && !sync) { return } - width, height, cfg := w.config() + size, cfg := w.getConfig() + if size != w.config.Size { + w.config.Size = size + w.w.Event(ConfigEvent{Config: w.config}) + } if cfg == (unit.Metric{}) { return } @@ -1380,13 +1443,10 @@ func (w *window) draw(sync bool) { // Use the surface as listener data for gio_onFrameDone. C.wl_callback_add_listener(w.lastFrameCallback, &C.gio_callback_listener, unsafe.Pointer(w.surf)) } - w.w.Event(FrameEvent{ + w.w.Event(frameEvent{ FrameEvent: system.FrameEvent{ - Now: time.Now(), - Size: image.Point{ - X: width, - Y: height, - }, + Now: time.Now(), + Size: w.config.Size, Metric: cfg, }, Sync: sync, @@ -1398,7 +1458,7 @@ func (w *window) setStage(s system.Stage) { return } w.stage = s - w.w.Event(system.StageEvent{s}) + w.w.Event(system.StageEvent{Stage: s}) } func (w *window) display() *C.struct_wl_display { @@ -1410,18 +1470,50 @@ func (w *window) surface() (*C.struct_wl_surface, int, int) { C.xdg_surface_ack_configure(w.wmSurf, w.serial) w.needAck = false } - width, height, scale := w.width, w.height, w.scale if w.newScale { - C.wl_surface_set_buffer_scale(w.surf, C.int32_t(scale)) + C.wl_surface_set_buffer_scale(w.surf, C.int32_t(w.scale)) w.newScale = false } - return w.surf, width * scale, height * scale + sz, _ := w.getConfig() + return w.surf, sz.X, sz.Y } func (w *window) ShowTextInput(show bool) {} -// Close the window. Not implemented for Wayland. -func (w *window) Close() {} +func (w *window) SetInputHint(_ key.InputHint) {} + +// Close the window. +func (w *window) Close() { + w.dead = true +} + +// Maximize the window. Not implemented for Wayland. +func (w *window) Maximize() {} + +// Center the window. Not implemented for Wayland. +func (w *window) Center() {} + +func (w *window) NewContext() (context, error) { + var firstErr error + if f := newWaylandVulkanContext; f != nil { + c, err := f(w) + if err == nil { + return c, nil + } + firstErr = err + } + if f := newWaylandEGLContext; f != nil { + c, err := f(w) + if err == nil { + return c, nil + } + firstErr = err + } + if firstErr != nil { + return nil, firstErr + } + return nil, errors.New("wayland: no available GPU backends") +} // detectUIScale reports the system UI scale, or 1.0 if it fails. func detectUIScale() float32 { diff --git a/vendor/gioui.org/app/internal/window/os_windows.go b/vendor/gioui.org/app/os_windows.go index 247f10a..ecbffb4 100644 --- a/vendor/gioui.org/app/internal/window/os_windows.go +++ b/vendor/gioui.org/app/os_windows.go @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Unlicense OR MIT -package window +package app import ( "errors" @@ -13,23 +13,23 @@ import ( "sync" "time" "unicode" - "unicode/utf16" "unsafe" syscall "golang.org/x/sys/windows" "gioui.org/app/internal/windows" "gioui.org/unit" + gowindows "golang.org/x/sys/windows" "gioui.org/f32" + "gioui.org/io/clipboard" "gioui.org/io/key" "gioui.org/io/pointer" "gioui.org/io/system" ) -type winConstraints struct { - minWidth, minHeight int32 - maxWidth, maxHeight int32 +type ViewEvent struct { + HWND uintptr } type winDeltas struct { @@ -40,69 +40,80 @@ type winDeltas struct { type window struct { hwnd syscall.Handle hdc syscall.Handle - w Callbacks - width int - height int + w *callbacks stage system.Stage - dead bool pointerBtns pointer.Buttons - mu sync.Mutex + // cursorIn tracks whether the cursor was inside the window according + // to the most recent WM_SETCURSOR. + cursorIn bool + cursor syscall.Handle + + // placement saves the previous window position when in full screen mode. + placement *windows.WindowPlacement + animating bool - minmax winConstraints deltas winDeltas - opts *Options + config Config } -const _WM_REDRAW = windows.WM_USER + 0 +const _WM_WAKEUP = windows.WM_USER + iota type gpuAPI struct { priority int - initializer func(w *window) (Context, error) + initializer func(w *window) (context, error) } -// backends is the list of potential Context -// implementations. -var backends []gpuAPI +// drivers is the list of potential Context implementations. +var drivers []gpuAPI // winMap maps win32 HWNDs to *windows. var winMap sync.Map +// iconID is the ID of the icon in the resource file. +const iconID = 1 + var resources struct { once sync.Once // handle is the module handle from GetModuleHandle. handle syscall.Handle // class is the Gio window class from RegisterClassEx. class uint16 - // cursor is the arrow cursor resource + // cursor is the arrow cursor resource. cursor syscall.Handle } -func Main() { +func osMain() { select {} } -func NewWindow(window Callbacks, opts *Options) error { +func newWindow(window *callbacks, options []Option) error { cerr := make(chan error) go func() { - // Call win32 API from a single OS thread. + // GetMessage and PeekMessage can filter on a window HWND, but + // then thread-specific messages such as WM_QUIT are ignored. + // Instead lock the thread so window messages arrive through + // unfiltered GetMessage calls. runtime.LockOSThread() - w, err := createNativeWindow(opts) + w, err := createNativeWindow() if err != nil { cerr <- err return } - defer w.destroy() cerr <- nil winMap.Store(w.hwnd, w) defer winMap.Delete(w.hwnd) w.w = window w.w.SetDriver(w) - defer w.w.Event(system.DestroyEvent{}) + w.w.Event(ViewEvent{HWND: uintptr(w.hwnd)}) + w.Configure(options) windows.ShowWindow(w.hwnd, windows.SW_SHOWDEFAULT) windows.SetForegroundWindow(w.hwnd) windows.SetFocus(w.hwnd) + // Since the window class for the cursor is null, + // set it here to show the cursor. + w.SetCursor(pointer.CursorDefault) if err := w.loop(); err != nil { panic(err) } @@ -118,17 +129,18 @@ func initResources() error { return err } resources.handle = hInst - curs, err := windows.LoadCursor(windows.IDC_ARROW) + c, err := windows.LoadCursor(windows.IDC_ARROW) if err != nil { return err } - resources.cursor = curs + resources.cursor = c + icon, _ := windows.LoadImage(hInst, iconID, windows.IMAGE_ICON, 0, 0, windows.LR_DEFAULTSIZE|windows.LR_SHARED) wcls := windows.WndClassEx{ CbSize: uint32(unsafe.Sizeof(windows.WndClassEx{})), Style: windows.CS_HREDRAW | windows.CS_VREDRAW | windows.CS_OWNDC, LpfnWndProc: syscall.NewCallback(windowProc), HInstance: hInst, - HCursor: curs, + HIcon: icon, LpszClassName: syscall.StringToUTF16Ptr("GioWindow"), } cls, err := windows.RegisterClassEx(&wcls) @@ -139,16 +151,7 @@ func initResources() error { return nil } -func getWindowConstraints(cfg unit.Metric, opts *Options, d winDeltas) winConstraints { - var minmax winConstraints - minmax.minWidth = int32(cfg.Px(opts.MinWidth)) - minmax.minHeight = int32(cfg.Px(opts.MinHeight)) - minmax.maxWidth = int32(cfg.Px(opts.MaxWidth)) - minmax.maxHeight = int32(cfg.Px(opts.MaxHeight)) - return minmax -} - -func createNativeWindow(opts *Options) (*window, error) { +func createNativeWindow() (*window, error) { var resErr error resources.once.Do(func() { resErr = initResources() @@ -156,28 +159,15 @@ func createNativeWindow(opts *Options) (*window, error) { if resErr != nil { return nil, resErr } - cfg := configForDC() - wr := windows.Rect{ - Right: int32(cfg.Px(opts.Width)), - Bottom: int32(cfg.Px(opts.Height)), - } dwStyle := uint32(windows.WS_OVERLAPPEDWINDOW) dwExStyle := uint32(windows.WS_EX_APPWINDOW | windows.WS_EX_WINDOWEDGE) - deltas := winDeltas{ - width: wr.Right, - height: wr.Bottom, - } - windows.AdjustWindowRectEx(&wr, dwStyle, 0, dwExStyle) - deltas.width = wr.Right - wr.Left - deltas.width - deltas.height = wr.Bottom - wr.Top - deltas.height hwnd, err := windows.CreateWindowEx(dwExStyle, resources.class, - opts.Title, + "", dwStyle|windows.WS_CLIPSIBLINGS|windows.WS_CLIPCHILDREN, windows.CW_USEDEFAULT, windows.CW_USEDEFAULT, - wr.Right-wr.Left, - wr.Bottom-wr.Top, + windows.CW_USEDEFAULT, windows.CW_USEDEFAULT, 0, 0, resources.handle, @@ -186,10 +176,7 @@ func createNativeWindow(opts *Options) (*window, error) { return nil, err } w := &window{ - hwnd: hwnd, - minmax: getWindowConstraints(cfg, opts, deltas), - deltas: deltas, - opts: opts, + hwnd: hwnd, } w.hdc, err = windows.GetDC(hwnd) if err != nil { @@ -210,7 +197,7 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr case windows.WM_UNICHAR: if wParam == windows.UNICODE_NOCHAR { // Tell the system that we accept WM_UNICHAR messages. - return 1 + return windows.TRUE } fallthrough case windows.WM_CHAR: @@ -218,29 +205,44 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr w.w.Event(key.EditEvent{Text: string(r)}) } // The message is processed. - return 1 + return windows.TRUE case windows.WM_DPICHANGED: // Let Windows know we're prepared for runtime DPI changes. - return 1 + return windows.TRUE case windows.WM_ERASEBKGND: // Avoid flickering between GPU content and background color. - return 1 - case windows.WM_KEYDOWN, windows.WM_SYSKEYDOWN: + return windows.TRUE + case windows.WM_KEYDOWN, windows.WM_KEYUP, windows.WM_SYSKEYDOWN, windows.WM_SYSKEYUP: if n, ok := convertKeyCode(wParam); ok { - w.w.Event(key.Event{Name: n, Modifiers: getModifiers()}) + e := key.Event{ + Name: n, + Modifiers: getModifiers(), + State: key.Press, + } + if msg == windows.WM_KEYUP || msg == windows.WM_SYSKEYUP { + e.State = key.Release + } + + w.w.Event(e) + + 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 + // such as cmd.exe and graphical debuggers also reserve F10. + return 0 + } } case windows.WM_LBUTTONDOWN: - w.pointerButton(pointer.ButtonLeft, true, lParam, getModifiers()) + w.pointerButton(pointer.ButtonPrimary, true, lParam, getModifiers()) case windows.WM_LBUTTONUP: - w.pointerButton(pointer.ButtonLeft, false, lParam, getModifiers()) + w.pointerButton(pointer.ButtonPrimary, false, lParam, getModifiers()) case windows.WM_RBUTTONDOWN: - w.pointerButton(pointer.ButtonRight, true, lParam, getModifiers()) + w.pointerButton(pointer.ButtonSecondary, true, lParam, getModifiers()) case windows.WM_RBUTTONUP: - w.pointerButton(pointer.ButtonRight, false, lParam, getModifiers()) + w.pointerButton(pointer.ButtonSecondary, false, lParam, getModifiers()) case windows.WM_MBUTTONDOWN: - w.pointerButton(pointer.ButtonMiddle, true, lParam, getModifiers()) + w.pointerButton(pointer.ButtonTertiary, true, lParam, getModifiers()) case windows.WM_MBUTTONUP: - w.pointerButton(pointer.ButtonMiddle, false, lParam, getModifiers()) + w.pointerButton(pointer.ButtonTertiary, false, lParam, getModifiers()) case windows.WM_CANCELMODE: w.w.Event(pointer.Event{ Type: pointer.Cancel, @@ -260,9 +262,19 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr Time: windows.GetMessageTime(), }) case windows.WM_MOUSEWHEEL: - w.scrollEvent(wParam, lParam) + w.scrollEvent(wParam, lParam, false) + case windows.WM_MOUSEHWHEEL: + w.scrollEvent(wParam, lParam, true) case windows.WM_DESTROY: - w.dead = true + w.w.Event(ViewEvent{}) + w.w.Event(system.DestroyEvent{}) + if w.hdc != 0 { + windows.ReleaseDC(w.hdc) + w.hdc = 0 + } + // The system destroys the HWND for us. + w.hwnd = 0 + windows.PostQuitMessage(0) case windows.WM_PAINT: w.draw(true) case windows.WM_SIZE: @@ -270,22 +282,51 @@ func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr case windows.SIZE_MINIMIZED: w.setStage(system.StagePaused) case windows.SIZE_MAXIMIZED, windows.SIZE_RESTORED: + var triggerEvent bool + // Check the window size change. + var r windows.Rect + windows.GetClientRect(w.hwnd, &r) + size := image.Point{ + X: int(r.Right - r.Left), + Y: int(r.Bottom - r.Top), + } + if size != w.config.Size { + w.config.Size = size + triggerEvent = true + } + // Check the window mode. + mode := w.config.Mode + w.updateWindowMode() + if mode != w.config.Mode { + triggerEvent = true + } + if triggerEvent { + w.w.Event(ConfigEvent{Config: w.config}) + } w.setStage(system.StageRunning) } case windows.WM_GETMINMAXINFO: mm := (*windows.MinMaxInfo)(unsafe.Pointer(uintptr(lParam))) - if w.minmax.minWidth > 0 || w.minmax.minHeight > 0 { + if p := w.config.MinSize; p.X > 0 || p.Y > 0 { mm.PtMinTrackSize = windows.Point{ - w.minmax.minWidth+w.deltas.width, - w.minmax.minHeight+w.deltas.height, + X: int32(p.X) + w.deltas.width, + Y: int32(p.Y) + w.deltas.height, } } - if w.minmax.maxWidth > 0 || w.minmax.maxHeight > 0 { + if p := w.config.MaxSize; p.X > 0 || p.Y > 0 { mm.PtMaxTrackSize = windows.Point{ - w.minmax.maxWidth+w.deltas.width, - w.minmax.maxHeight+w.deltas.height, + X: int32(p.X) + w.deltas.width, + Y: int32(p.Y) + w.deltas.height, } } + case windows.WM_SETCURSOR: + w.cursorIn = (lParam & 0xffff) == windows.HTCLIENT + if w.cursorIn { + windows.SetCursor(w.cursor) + return windows.TRUE + } + case _WM_WAKEUP: + w.w.Event(wakeupEvent{}) } return windows.DefWindowProc(hwnd, msg, wParam, lParam) @@ -341,7 +382,7 @@ func coordsFromlParam(lParam uintptr) (int, int) { return x, y } -func (w *window) scrollEvent(wParam, lParam uintptr) { +func (w *window) scrollEvent(wParam, lParam uintptr, horizontal bool) { x, y := coordsFromlParam(lParam) // The WM_MOUSEWHEEL coordinates are in screen coordinates, in contrast // to other mouse events. @@ -349,12 +390,18 @@ func (w *window) scrollEvent(wParam, lParam uintptr) { windows.ScreenToClient(w.hwnd, &np) p := f32.Point{X: float32(np.X), Y: float32(np.Y)} dist := float32(int16(wParam >> 16)) + var sp f32.Point + if horizontal { + sp.X = dist + } else { + sp.Y = -dist + } w.w.Event(pointer.Event{ Type: pointer.Scroll, Source: pointer.Mouse, Position: p, Buttons: w.pointerBtns, - Scroll: f32.Point{Y: -dist}, + Scroll: sp, Time: windows.GetMessageTime(), }) } @@ -362,18 +409,19 @@ func (w *window) scrollEvent(wParam, lParam uintptr) { // Adapted from https://blogs.msdn.microsoft.com/oldnewthing/20060126-00/?p=32513/ func (w *window) loop() error { msg := new(windows.Msg) - for !w.dead { - w.mu.Lock() +loop: + for { anim := w.animating - w.mu.Unlock() - if anim && !windows.PeekMessage(msg, w.hwnd, 0, 0, windows.PM_NOREMOVE) { + if anim && !windows.PeekMessage(msg, 0, 0, 0, windows.PM_NOREMOVE) { w.draw(false) continue } - windows.GetMessage(msg, w.hwnd, 0, 0) - if msg.Message == windows.WM_QUIT { - windows.PostQuitMessage(msg.WParam) - break + switch ret := windows.GetMessage(msg, 0, 0, 0); ret { + case -1: + return errors.New("GetMessage failed") + case 0: + // WM_QUIT received. + break loop } windows.TranslateMessage(msg) windows.DispatchMessage(msg) @@ -382,65 +430,44 @@ func (w *window) loop() error { } func (w *window) SetAnimating(anim bool) { - w.mu.Lock() w.animating = anim - w.mu.Unlock() - if anim { - w.postRedraw() - } } -func (w *window) postRedraw() { - if err := windows.PostMessage(w.hwnd, _WM_REDRAW, 0, 0); err != nil { +func (w *window) Wakeup() { + if err := windows.PostMessage(w.hwnd, _WM_WAKEUP, 0, 0); err != nil { panic(err) } } func (w *window) setStage(s system.Stage) { - w.stage = s - w.w.Event(system.StageEvent{Stage: s}) + if s != w.stage { + w.stage = s + w.w.Event(system.StageEvent{Stage: s}) + } } func (w *window) draw(sync bool) { - var r windows.Rect - windows.GetClientRect(w.hwnd, &r) - w.width = int(r.Right - r.Left) - w.height = int(r.Bottom - r.Top) - if w.width == 0 || w.height == 0 { + if w.config.Size.X == 0 || w.config.Size.Y == 0 { return } - cfg := configForDC() - w.minmax = getWindowConstraints(cfg, w.opts, w.deltas) - w.w.Event(FrameEvent{ + dpi := windows.GetWindowDPI(w.hwnd) + cfg := configForDPI(dpi) + w.w.Event(frameEvent{ FrameEvent: system.FrameEvent{ - Now: time.Now(), - Size: image.Point{ - X: w.width, - Y: w.height, - }, + Now: time.Now(), + Size: w.config.Size, Metric: cfg, }, Sync: sync, }) } -func (w *window) destroy() { - if w.hdc != 0 { - windows.ReleaseDC(w.hdc) - w.hdc = 0 - } - if w.hwnd != 0 { - windows.DestroyWindow(w.hwnd) - w.hwnd = 0 - } -} - -func (w *window) NewContext() (Context, error) { - sort.Slice(backends, func(i, j int) bool { - return backends[i].priority < backends[j].priority +func (w *window) NewContext() (context, error) { + sort.Slice(drivers, func(i, j int) bool { + return drivers[i].priority < drivers[j].priority }) var errs []string - for _, b := range backends { + for _, b := range drivers { ctx, err := b.initializer(w) if err == nil { return ctx, nil @@ -450,7 +477,7 @@ func (w *window) NewContext() (Context, error) { if len(errs) > 0 { return nil, fmt.Errorf("NewContext: failed to create a GPU device, tried: %s", strings.Join(errs, ", ")) } - return nil, errors.New("NewContext: no available backends") + return nil, errors.New("NewContext: no available GPU drivers") } func (w *window) ReadClipboard() { @@ -471,25 +498,142 @@ func (w *window) readClipboard() error { return err } defer windows.GlobalUnlock(mem) - // Look for terminating null character. - n := 0 - for { - ch := *(*uint16)(unsafe.Pointer(ptr + uintptr(n)*2)) - if ch == 0 { - break + content := gowindows.UTF16PtrToString((*uint16)(unsafe.Pointer(ptr))) + w.w.Event(clipboard.Event{Text: content}) + return nil +} + +func (w *window) updateWindowMode() { + p := windows.GetWindowPlacement(w.hwnd) + r := p.Rect() + mi := windows.GetMonitorInfo(w.hwnd) + if r == mi.Monitor { + w.config.Mode = Fullscreen + } else { + w.config.Mode = Windowed + } +} + +func (w *window) Configure(options []Option) { + dpi := windows.GetSystemDPI() + cfg := configForDPI(dpi) + prev := w.config + w.updateWindowMode() + cnf := w.config + cnf.apply(cfg, options) + + if cnf.Mode != Fullscreen && prev.Size != cnf.Size { + width := int32(cnf.Size.X) + height := int32(cnf.Size.Y) + w.config.Size = cnf.Size + + // Include the window decorations. + wr := windows.Rect{ + Right: width, + Bottom: height, } - n++ + dwStyle := uint32(windows.WS_OVERLAPPEDWINDOW) + dwExStyle := uint32(windows.WS_EX_APPWINDOW | windows.WS_EX_WINDOWEDGE) + windows.AdjustWindowRectEx(&wr, dwStyle, 0, dwExStyle) + + dw, dh := width, height + width = wr.Right - wr.Left + height = wr.Bottom - wr.Top + w.deltas.width = width - dw + w.deltas.height = height - dh + + windows.MoveWindow(w.hwnd, 0, 0, width, height, true) + } + if prev.MinSize != cnf.MinSize { + w.config.MinSize = cnf.MinSize + } + if prev.MaxSize != cnf.MaxSize { + w.config.MaxSize = cnf.MaxSize + } + if prev.Title != cnf.Title { + w.config.Title = cnf.Title + windows.SetWindowText(w.hwnd, cnf.Title) + } + if prev.Mode != cnf.Mode { + w.SetWindowMode(cnf.Mode) + } + if w.config != prev { + w.w.Event(ConfigEvent{Config: w.config}) + } +} + +// Maximize the window. It will have no effect when in fullscreen mode. +func (w *window) Maximize() { + if w.config.Mode == Fullscreen { + return + } + style := windows.GetWindowLong(w.hwnd) + windows.SetWindowLong(w.hwnd, windows.GWL_STYLE, style|windows.WS_OVERLAPPEDWINDOW|windows.WS_MAXIMIZE) + mi := windows.GetMonitorInfo(w.hwnd) + windows.SetWindowPos(w.hwnd, 0, + mi.Monitor.Left, mi.Monitor.Top, + mi.Monitor.Right-mi.Monitor.Left, + mi.Monitor.Bottom-mi.Monitor.Top, + windows.SWP_NOOWNERZORDER|windows.SWP_FRAMECHANGED, + ) +} + +// Center will place window at monitor center. +func (w *window) Center() { + // Make sure that the window is sizeable + style := windows.GetWindowLong(w.hwnd) & (^uintptr(windows.WS_MAXIMIZE)) + windows.SetWindowLong(w.hwnd, windows.GWL_STYLE, style|windows.WS_OVERLAPPEDWINDOW) + + // Find with/height including the window decorations. + wr := windows.Rect{ + Right: int32(w.config.Size.X), + Bottom: int32(w.config.Size.Y), + } + dwStyle := uint32(windows.WS_OVERLAPPEDWINDOW) + dwExStyle := uint32(windows.WS_EX_APPWINDOW | windows.WS_EX_WINDOWEDGE) + windows.AdjustWindowRectEx(&wr, dwStyle, 0, dwExStyle) + width := wr.Right - wr.Left + height := wr.Bottom - wr.Top + + // Move to center of current monitor + mi := windows.GetMonitorInfo(w.hwnd).Monitor + x := mi.Left + (mi.Right-mi.Left-width)/2 + y := mi.Top + (mi.Bottom-mi.Top-height)/2 + windows.MoveWindow(w.hwnd, x, y, width, height, true) +} + +func (w *window) SetWindowMode(mode WindowMode) { + // https://devblogs.microsoft.com/oldnewthing/20100412-00/?p=14353 + switch mode { + case Windowed: + if w.placement == nil { + return + } + w.config.Mode = Windowed + windows.SetWindowPlacement(w.hwnd, w.placement) + w.placement = nil + style := windows.GetWindowLong(w.hwnd) + windows.SetWindowLong(w.hwnd, windows.GWL_STYLE, style|windows.WS_OVERLAPPEDWINDOW) + windows.SetWindowPos(w.hwnd, windows.HWND_TOPMOST, + 0, 0, 0, 0, + windows.SWP_NOOWNERZORDER|windows.SWP_FRAMECHANGED, + ) + case Fullscreen: + if w.placement != nil { + return + } + w.config.Mode = Fullscreen + w.placement = windows.GetWindowPlacement(w.hwnd) + style := windows.GetWindowLong(w.hwnd) + windows.SetWindowLong(w.hwnd, windows.GWL_STYLE, style&^windows.WS_OVERLAPPEDWINDOW) + mi := windows.GetMonitorInfo(w.hwnd) + windows.SetWindowPos(w.hwnd, 0, + mi.Monitor.Left, mi.Monitor.Top, + mi.Monitor.Right-mi.Monitor.Left, + mi.Monitor.Bottom-mi.Monitor.Top, + windows.SWP_NOOWNERZORDER|windows.SWP_FRAMECHANGED, + ) } - var u16 []uint16 - hdr := (*reflect.SliceHeader)(unsafe.Pointer(&u16)) - hdr.Data = ptr - hdr.Cap = n - hdr.Len = n - content := string(utf16.Decode(u16)) - go func() { - w.w.Event(system.ClipboardEvent{Text: content}) - }() - return nil } func (w *window) WriteClipboard(s string) { @@ -497,9 +641,6 @@ func (w *window) WriteClipboard(s string) { } func (w *window) writeClipboard(s string) error { - u16 := utf16.Encode([]rune(s)) - // Data must be null terminated. - u16 = append(u16, 0) if err := windows.OpenClipboard(w.hwnd); err != nil { return err } @@ -507,6 +648,10 @@ func (w *window) writeClipboard(s string) error { if err := windows.EmptyClipboard(); err != nil { return err } + u16, err := gowindows.UTF16FromString(s) + if err != nil { + return err + } n := len(u16) * int(unsafe.Sizeof(u16[0])) mem, err := windows.GlobalAlloc(n) if err != nil { @@ -531,20 +676,64 @@ func (w *window) writeClipboard(s string) error { return nil } +func (w *window) SetCursor(name pointer.CursorName) { + c, err := loadCursor(name) + if err != nil { + c = resources.cursor + } + w.cursor = c + if w.cursorIn { + windows.SetCursor(w.cursor) + } +} + +func loadCursor(name pointer.CursorName) (syscall.Handle, error) { + var curID uint16 + switch name { + default: + fallthrough + case pointer.CursorDefault: + return resources.cursor, nil + case pointer.CursorText: + curID = windows.IDC_IBEAM + case pointer.CursorPointer: + curID = windows.IDC_HAND + case pointer.CursorCrossHair: + curID = windows.IDC_CROSS + case pointer.CursorColResize: + curID = windows.IDC_SIZEWE + case pointer.CursorRowResize: + curID = windows.IDC_SIZENS + case pointer.CursorGrab: + curID = windows.IDC_SIZEALL + case pointer.CursorNone: + return 0, nil + } + return windows.LoadCursor(curID) +} + func (w *window) ShowTextInput(show bool) {} +func (w *window) SetInputHint(_ key.InputHint) {} + func (w *window) HDC() syscall.Handle { return w.hdc } func (w *window) HWND() (syscall.Handle, int, int) { - return w.hwnd, w.width, w.height + return w.hwnd, w.config.Size.X, w.config.Size.Y } func (w *window) Close() { windows.PostMessage(w.hwnd, windows.WM_CLOSE, 0, 0) } +func (w *window) Raise() { + windows.SetForegroundWindow(w.hwnd) + windows.SetWindowPos(w.hwnd, windows.HWND_TOPMOST, 0, 0, 0, 0, + windows.SWP_NOMOVE|windows.SWP_NOSIZE|windows.SWP_SHOWWINDOW) +} + func convertKeyCode(code uintptr) (string, bool) { if '0' <= code && code <= '9' || 'A' <= code && code <= 'Z' { return string(rune(code)), true @@ -602,7 +791,7 @@ func convertKeyCode(code uintptr) (string, bool) { case windows.VK_TAB: r = key.NameTab case windows.VK_SPACE: - r = "Space" + r = key.NameSpace case windows.VK_OEM_1: r = ";" case windows.VK_OEM_PLUS: @@ -631,8 +820,7 @@ func convertKeyCode(code uintptr) (string, bool) { return r, true } -func configForDC() unit.Metric { - dpi := windows.GetSystemDPI() +func configForDPI(dpi int) unit.Metric { const inchPrDp = 1.0 / 96.0 ppdp := float32(dpi) * inchPrDp return unit.Metric{ @@ -640,3 +828,5 @@ func configForDC() unit.Metric { PxPerSp: ppdp, } } + +func (_ ViewEvent) ImplementsEvent() {} diff --git a/vendor/gioui.org/app/internal/window/os_x11.go b/vendor/gioui.org/app/os_x11.go index 69108c6..1c75247 100644 --- a/vendor/gioui.org/app/internal/window/os_x11.go +++ b/vendor/gioui.org/app/os_x11.go @@ -1,14 +1,16 @@ // SPDX-License-Identifier: Unlicense OR MIT -// +build linux,!android,!nox11 freebsd openbsd +//go:build ((linux && !android) || freebsd || openbsd) && !nox11 +// +build linux,!android freebsd openbsd +// +build !nox11 -package window +package app /* -#cgo openbsd CFLAGS: -I/usr/X11R6/include -I/usr/local/include -#cgo openbsd LDFLAGS: -L/usr/X11R6/lib -L/usr/local/lib -#cgo freebsd openbsd LDFLAGS: -lX11 -lxkbcommon -lxkbcommon-x11 -lX11-xcb -#cgo linux pkg-config: x11 xkbcommon xkbcommon-x11 x11-xcb +#cgo freebsd openbsd CFLAGS: -I/usr/X11R6/include -I/usr/local/include +#cgo freebsd openbsd LDFLAGS: -L/usr/X11R6/lib -L/usr/local/lib +#cgo freebsd openbsd LDFLAGS: -lX11 -lxkbcommon -lxkbcommon-x11 -lX11-xcb -lXcursor -lXfixes +#cgo linux pkg-config: x11 xkbcommon xkbcommon-x11 x11-xcb xcursor xfixes #include <stdlib.h> #include <locale.h> @@ -18,6 +20,8 @@ package window #include <X11/Xresource.h> #include <X11/XKBlib.h> #include <X11/Xlib-xcb.h> +#include <X11/extensions/Xfixes.h> +#include <X11/Xcursor/Xcursor.h> #include <xkbcommon/xkbcommon-x11.h> */ @@ -34,17 +38,24 @@ import ( "unsafe" "gioui.org/f32" + "gioui.org/io/clipboard" "gioui.org/io/key" "gioui.org/io/pointer" "gioui.org/io/system" "gioui.org/unit" - "gioui.org/app/internal/xkb" syscall "golang.org/x/sys/unix" + + "gioui.org/app/internal/xkb" +) + +const ( + _NET_WM_STATE_REMOVE = 0 + _NET_WM_STATE_ADD = 1 ) type x11Window struct { - w Callbacks + w *callbacks x *C.Display xkb *xkb.Context xkbEventBase C.int @@ -53,68 +64,213 @@ type x11Window struct { atoms struct { // "UTF8_STRING". utf8string C.Atom + // "text/plain;charset=utf-8". + plaintext C.Atom // "TARGETS" targets C.Atom // "CLIPBOARD". clipboard C.Atom + // "PRIMARY". + primary C.Atom // "CLIPBOARD_CONTENT", the clipboard destination property. clipboardContent C.Atom // "WM_DELETE_WINDOW" evDelWindow C.Atom // "ATOM" atom C.Atom + // "GTK_TEXT_BUFFER_CONTENTS" + gtk_text_buffer_contents C.Atom + // "_NET_WM_NAME" + wmName C.Atom + // "_NET_WM_STATE" + wmState C.Atom + // "_NET_WM_STATE_FULLSCREEN" + wmStateFullscreen C.Atom + // "_NET_ACTIVE_WINDOW" + wmActiveWindow C.Atom + // _NET_WM_STATE_MAXIMIZED_HORZ + wmStateMaximizedHorz C.Atom + // _NET_WM_STATE_MAXIMIZED_VERT + wmStateMaximizedVert C.Atom } stage system.Stage - cfg unit.Metric - width int - height int + metric unit.Metric notify struct { read, write int } dead bool - mu sync.Mutex animating bool pointerBtns pointer.Buttons clipboard struct { - read bool - write *string content []byte } + cursor pointer.CursorName + config Config + + wakeups chan struct{} +} + +var ( + newX11EGLContext func(w *x11Window) (context, error) + newX11VulkanContext func(w *x11Window) (context, error) +) + +func (w *x11Window) NewContext() (context, error) { + var firstErr error + if f := newX11VulkanContext; f != nil { + c, err := f(w) + if err == nil { + return c, nil + } + firstErr = err + } + if f := newX11EGLContext; f != nil { + c, err := f(w) + if err == nil { + return c, nil + } + firstErr = err + } + if firstErr != nil { + return nil, firstErr + } + return nil, errors.New("x11: no available GPU backends") } func (w *x11Window) SetAnimating(anim bool) { - w.mu.Lock() w.animating = anim - w.mu.Unlock() - if anim { - w.wakeup() - } } func (w *x11Window) ReadClipboard() { - w.mu.Lock() - w.clipboard.read = true - w.mu.Unlock() - w.wakeup() + C.XDeleteProperty(w.x, w.xw, w.atoms.clipboardContent) + C.XConvertSelection(w.x, w.atoms.clipboard, w.atoms.utf8string, w.atoms.clipboardContent, w.xw, C.CurrentTime) } func (w *x11Window) WriteClipboard(s string) { - w.mu.Lock() - w.clipboard.write = &s - w.mu.Unlock() - w.wakeup() + w.clipboard.content = []byte(s) + C.XSetSelectionOwner(w.x, w.atoms.clipboard, w.xw, C.CurrentTime) + C.XSetSelectionOwner(w.x, w.atoms.primary, w.xw, C.CurrentTime) +} + +func (w *x11Window) Configure(options []Option) { + var shints C.XSizeHints + prev := w.config + cnf := w.config + cnf.apply(w.metric, options) + if prev.MinSize != cnf.MinSize { + w.config.MinSize = cnf.MinSize + shints.min_width = C.int(cnf.MinSize.X) + shints.min_height = C.int(cnf.MinSize.Y) + shints.flags = C.PMinSize + } + if prev.MaxSize != cnf.MaxSize { + w.config.MaxSize = cnf.MaxSize + shints.max_width = C.int(cnf.MaxSize.X) + shints.max_height = C.int(cnf.MaxSize.Y) + shints.flags = shints.flags | C.PMaxSize + } + if shints.flags != 0 { + C.XSetWMNormalHints(w.x, w.xw, &shints) + } + + if cnf.Mode != Fullscreen && prev.Size != cnf.Size { + w.config.Size = cnf.Size + C.XResizeWindow(w.x, w.xw, C.uint(cnf.Size.X), C.uint(cnf.Size.Y)) + } + + if prev.Title != cnf.Title { + title := cnf.Title + ctitle := C.CString(title) + defer C.free(unsafe.Pointer(ctitle)) + C.XStoreName(w.x, w.xw, ctitle) + // set _NET_WM_NAME as well for UTF-8 support in window title. + C.XSetTextProperty(w.x, w.xw, + &C.XTextProperty{ + value: (*C.uchar)(unsafe.Pointer(ctitle)), + encoding: w.atoms.utf8string, + format: 8, + nitems: C.ulong(len(title)), + }, + w.atoms.wmName) + } + + if prev.Mode != cnf.Mode { + w.SetWindowMode(cnf.Mode) + } + if w.config != prev { + w.w.Event(ConfigEvent{Config: w.config}) + } +} + +func (w *x11Window) Raise() { + var xev C.XEvent + ev := (*C.XClientMessageEvent)(unsafe.Pointer(&xev)) + *ev = C.XClientMessageEvent{ + _type: C.ClientMessage, + display: w.x, + window: w.xw, + message_type: w.atoms.wmActiveWindow, + format: 32, + } + C.XSendEvent( + w.x, + C.XDefaultRootWindow(w.x), // MUST be the root window + C.False, + C.SubstructureNotifyMask|C.SubstructureRedirectMask, + &xev, + ) + C.XMapRaised(w.display(), w.xw) +} + +func (w *x11Window) SetCursor(name pointer.CursorName) { + switch name { + case pointer.CursorNone: + w.cursor = name + C.XFixesHideCursor(w.x, w.xw) + return + case pointer.CursorGrab: + name = "hand1" + } + if w.cursor == pointer.CursorNone { + C.XFixesShowCursor(w.x, w.xw) + } + cname := C.CString(string(name)) + defer C.free(unsafe.Pointer(cname)) + c := C.XcursorLibraryLoadCursor(w.x, cname) + if c == 0 { + name = pointer.CursorDefault + } + w.cursor = name + // If c if null (i.e. name was not found), + // XDefineCursor will use the default cursor. + C.XDefineCursor(w.x, w.xw, c) +} + +func (w *x11Window) SetWindowMode(mode WindowMode) { + var action C.long + switch mode { + case Windowed: + action = _NET_WM_STATE_REMOVE + case Fullscreen: + action = _NET_WM_STATE_ADD + default: + return + } + w.config.Mode = mode + // "A Client wishing to change the state of a window MUST send + // a _NET_WM_STATE client message to the root window." + w.sendWMStateEvent(action, w.atoms.wmStateFullscreen, 0) } func (w *x11Window) ShowTextInput(show bool) {} +func (w *x11Window) SetInputHint(_ key.InputHint) {} + // Close the window. func (w *x11Window) Close() { - w.mu.Lock() - defer w.mu.Unlock() - var xev C.XEvent ev := (*C.XClientMessageEvent)(unsafe.Pointer(&xev)) *ev = C.XClientMessageEvent{ @@ -130,9 +286,62 @@ func (w *x11Window) Close() { C.XSendEvent(w.x, w.xw, C.False, C.NoEventMask, &xev) } +// Maximize the window. +func (w *x11Window) Maximize() { + w.sendWMStateEvent(_NET_WM_STATE_ADD, w.atoms.wmStateMaximizedHorz, w.atoms.wmStateMaximizedVert) +} + +// Center the window. +func (w *x11Window) Center() { + screen := C.XDefaultScreen(w.x) + width := C.XDisplayWidth(w.x, screen) + height := C.XDisplayHeight(w.x, screen) + + var attrs C.XWindowAttributes + C.XGetWindowAttributes(w.x, w.xw, &attrs) + width -= attrs.border_width + height -= attrs.border_width + + sz := w.config.Size + x := (int(width) - sz.X) / 2 + y := (int(height) - sz.Y) / 2 + + C.XMoveResizeWindow(w.x, w.xw, C.int(x), C.int(y), C.uint(sz.X), C.uint(sz.Y)) +} + +// action is one of _NET_WM_STATE_REMOVE, _NET_WM_STATE_ADD. +func (w *x11Window) sendWMStateEvent(action C.long, atom1, atom2 C.ulong) { + var xev C.XEvent + ev := (*C.XClientMessageEvent)(unsafe.Pointer(&xev)) + *ev = C.XClientMessageEvent{ + _type: C.ClientMessage, + display: w.x, + window: w.xw, + message_type: w.atoms.wmState, + format: 32, + } + data := (*[5]C.long)(unsafe.Pointer(&ev.data)) + data[0] = C.long(action) + data[1] = C.long(atom1) + data[2] = C.long(atom2) + data[3] = 1 // application + + C.XSendEvent( + w.x, + C.XDefaultRootWindow(w.x), // MUST be the root window + C.False, + C.SubstructureNotifyMask|C.SubstructureRedirectMask, + &xev, + ) +} + var x11OneByte = make([]byte, 1) -func (w *x11Window) wakeup() { +func (w *x11Window) Wakeup() { + select { + case w.wakeups <- struct{}{}: + default: + } if _, err := syscall.Write(w.notify.write, x11OneByte); err != nil && err != syscall.EAGAIN { panic(fmt.Errorf("failed to write to pipe: %v", err)) } @@ -143,7 +352,7 @@ func (w *x11Window) display() *C.Display { } func (w *x11Window) window() (C.Window, int, int) { - return w.xw, w.width, w.height + return w.xw, w.config.Size.X, w.config.Size.Y } func (w *x11Window) setStage(s system.Stage) { @@ -151,7 +360,7 @@ func (w *x11Window) setStage(s system.Stage) { return } w.stage = s - w.w.Event(system.StageEvent{s}) + w.w.Event(system.StageEvent{Stage: s}) } func (w *x11Window) loop() { @@ -174,9 +383,7 @@ loop: // This fixes an issue on Xephyr where on startup XPending() > 0 but // poll will still block. This also prevents no-op calls to poll. if syn = h.handleEvents(); !syn { - w.mu.Lock() anim = w.animating - w.mu.Unlock() if !anim { // Clear poll events. *xEvents = 0 @@ -205,34 +412,22 @@ loop: panic(fmt.Errorf("x11 loop: read from notify pipe failed: %w", err)) } } + select { + case <-w.wakeups: + w.w.Event(wakeupEvent{}) + default: + } - if anim || syn { - w.w.Event(FrameEvent{ + if (anim || syn) && w.config.Size.X != 0 && w.config.Size.Y != 0 { + w.w.Event(frameEvent{ FrameEvent: system.FrameEvent{ - Now: time.Now(), - Size: image.Point{ - X: w.width, - Y: w.height, - }, - Metric: w.cfg, + Now: time.Now(), + Size: w.config.Size, + Metric: w.metric, }, Sync: syn, }) } - w.mu.Lock() - readClipboard := w.clipboard.read - writeClipboard := w.clipboard.write - w.clipboard.read = false - w.clipboard.write = nil - w.mu.Unlock() - if readClipboard { - C.XDeleteProperty(w.x, w.xw, w.atoms.clipboardContent) - C.XConvertSelection(w.x, w.atoms.clipboard, w.atoms.utf8string, w.atoms.clipboardContent, w.xw, C.CurrentTime) - } - if writeClipboard != nil { - w.clipboard.content = []byte(*writeClipboard) - C.XSetSelectionOwner(w.x, w.atoms.clipboard, w.xw, C.CurrentTime) - } } w.w.Event(system.DestroyEvent{Err: nil}) } @@ -301,12 +496,15 @@ func (h *x11EventHandler) handleEvents() bool { h.w.xkb.UpdateMask(uint32(state.base_mods), uint32(state.latched_mods), uint32(state.locked_mods), uint32(state.base_group), uint32(state.latched_group), uint32(state.locked_group)) } - case C.KeyPress: + case C.KeyPress, C.KeyRelease: + ks := key.Press + if _type == C.KeyRelease { + ks = key.Release + } kevt := (*C.XKeyPressedEvent)(unsafe.Pointer(xev)) - for _, e := range h.w.xkb.DispatchKey(uint32(kevt.keycode)) { + for _, e := range h.w.xkb.DispatchKey(uint32(kevt.keycode), ks) { w.w.Event(e) } - case C.KeyRelease: case C.ButtonPress, C.ButtonRelease: bevt := (*C.XButtonEvent)(unsafe.Pointer(xev)) ev := pointer.Event{ @@ -326,11 +524,11 @@ func (h *x11EventHandler) handleEvents() bool { const scrollScale = 10 switch bevt.button { case C.Button1: - btn = pointer.ButtonLeft + btn = pointer.ButtonPrimary case C.Button2: - btn = pointer.ButtonMiddle + btn = pointer.ButtonTertiary case C.Button3: - btn = pointer.ButtonRight + btn = pointer.ButtonSecondary case C.Button4: // scroll up ev.Type = pointer.Scroll @@ -339,6 +537,15 @@ func (h *x11EventHandler) handleEvents() bool { // scroll down ev.Type = pointer.Scroll ev.Scroll.Y = +scrollScale + case 6: + // http://xahlee.info/linux/linux_x11_mouse_button_number.html + // scroll left + ev.Type = pointer.Scroll + ev.Scroll.X = -scrollScale * 2 + case 7: + // scroll right + ev.Type = pointer.Scroll + ev.Scroll.X = +scrollScale * 2 default: continue } @@ -372,8 +579,10 @@ func (h *x11EventHandler) handleEvents() bool { w.w.Event(key.FocusEvent{Focus: false}) case C.ConfigureNotify: // window configuration change cevt := (*C.XConfigureEvent)(unsafe.Pointer(xev)) - w.width = int(cevt.width) - w.height = int(cevt.height) + if sz := image.Pt(int(cevt.width), int(cevt.height)); sz != w.config.Size { + w.config.Size = sz + w.w.Event(ConfigEvent{Config: w.config}) + } // redraw will be done by a later expose event case C.SelectionNotify: cevt := (*C.XSelectionEvent)(unsafe.Pointer(xev)) @@ -394,10 +603,10 @@ func (h *x11EventHandler) handleEvents() bool { break } str := C.GoStringN((*C.char)(unsafe.Pointer(text.value)), C.int(text.nitems)) - w.w.Event(system.ClipboardEvent{Text: str}) + w.w.Event(clipboard.Event{Text: str}) case C.SelectionRequest: cevt := (*C.XSelectionRequestEvent)(unsafe.Pointer(xev)) - if cevt.selection != w.atoms.clipboard || cevt.property == C.None { + if (cevt.selection != w.atoms.clipboard && cevt.selection != w.atoms.primary) || cevt.property == C.None { // Unsupported clipboard or obsolete requestor. break } @@ -418,21 +627,27 @@ func (h *x11EventHandler) handleEvents() bool { switch cevt.target { case w.atoms.targets: // The requestor wants the supported clipboard - // formats. First write the formats... - formats := []uint32{uint32(w.atoms.utf8string)} + // formats. First write the targets... + formats := [...]C.long{ + C.long(w.atoms.targets), + C.long(w.atoms.utf8string), + C.long(w.atoms.plaintext), + // GTK clients need this. + C.long(w.atoms.gtk_text_buffer_contents), + } C.XChangeProperty(w.x, cevt.requestor, cevt.property, w.atoms.atom, 32 /* bitwidth of formats */, C.PropModeReplace, - (*C.uchar)(unsafe.Pointer(&formats[0])), C.int(len(formats)), + (*C.uchar)(unsafe.Pointer(&formats)), C.int(len(formats)), ) // ...then notify the requestor. notify() - case w.atoms.utf8string: + case w.atoms.plaintext, w.atoms.utf8string, w.atoms.gtk_text_buffer_contents: content := w.clipboard.content var ptr *C.uchar if len(content) > 0 { ptr = (*C.uchar)(unsafe.Pointer(&content[0])) } - C.XChangeProperty(w.x, cevt.requestor, cevt.property, w.atoms.utf8string, + C.XChangeProperty(w.x, cevt.requestor, cevt.property, cevt.target, 8 /* bitwidth */, C.PropModeReplace, ptr, C.int(len(content)), ) @@ -458,7 +673,7 @@ func init() { x11Driver = newX11Window } -func newX11Window(gioWin Callbacks, opts *Options) error { +func newX11Window(gioWin *callbacks, options []Option) error { var err error pipe := make([]int, 2) @@ -498,6 +713,10 @@ func newX11Window(gioWin Callbacks, opts *Options) error { ppsp := x11DetectUIScale(dpy) cfg := unit.Metric{PxPerDp: ppsp, PxPerSp: ppsp} + // Only use cnf for getting the window size. + var cnf Config + cnf.apply(cfg, options) + swa := C.XSetWindowAttributes{ event_mask: C.ExposureMask | C.FocusChangeMask | // update C.KeyPressMask | C.KeyReleaseMask | // keyboard @@ -508,17 +727,17 @@ func newX11Window(gioWin Callbacks, opts *Options) error { override_redirect: C.False, } win := C.XCreateWindow(dpy, C.XDefaultRootWindow(dpy), - 0, 0, C.uint(cfg.Px(opts.Width)), C.uint(cfg.Px(opts.Height)), + 0, 0, C.uint(cnf.Size.X), C.uint(cnf.Size.Y), 0, C.CopyFromParent, C.InputOutput, nil, C.CWEventMask|C.CWBackPixmap|C.CWOverrideRedirect, &swa) w := &x11Window{ w: gioWin, x: dpy, xw: win, - width: cfg.Px(opts.Width), - height: cfg.Px(opts.Height), - cfg: cfg, + metric: cfg, xkb: xkb, xkbEventBase: xkbEventBase, + wakeups: make(chan struct{}, 1), + config: Config{Size: cnf.Size}, } w.notify.read = pipe[0] w.notify.write = pipe[1] @@ -533,57 +752,40 @@ func newX11Window(gioWin Callbacks, opts *Options) error { hints.flags = C.InputHint C.XSetWMHints(dpy, win, &hints) - var shints C.XSizeHints - if opts.MinWidth.V != 0 || opts.MinHeight.V != 0 { - shints.min_width = C.int(cfg.Px(opts.MinWidth)) - shints.min_height = C.int(cfg.Px(opts.MinHeight)) - shints.flags = C.PMinSize - } - if opts.MaxWidth.V != 0 || opts.MaxHeight.V != 0 { - shints.max_width = C.int(cfg.Px(opts.MaxWidth)) - shints.max_height = C.int(cfg.Px(opts.MaxHeight)) - shints.flags = shints.flags | C.PMaxSize - } - if shints.flags != 0 { - C.XSetWMNormalHints(dpy, win, &shints) - } - name := C.CString(filepath.Base(os.Args[0])) defer C.free(unsafe.Pointer(name)) wmhints := C.XClassHint{name, name} C.XSetClassHint(dpy, win, &wmhints) w.atoms.utf8string = w.atom("UTF8_STRING", false) + w.atoms.plaintext = w.atom("text/plain;charset=utf-8", false) + w.atoms.gtk_text_buffer_contents = w.atom("GTK_TEXT_BUFFER_CONTENTS", false) w.atoms.evDelWindow = w.atom("WM_DELETE_WINDOW", false) w.atoms.clipboard = w.atom("CLIPBOARD", false) + w.atoms.primary = w.atom("PRIMARY", false) w.atoms.clipboardContent = w.atom("CLIPBOARD_CONTENT", false) w.atoms.atom = w.atom("ATOM", false) w.atoms.targets = w.atom("TARGETS", false) - - // set the name - ctitle := C.CString(opts.Title) - defer C.free(unsafe.Pointer(ctitle)) - C.XStoreName(dpy, win, ctitle) - // set _NET_WM_NAME as well for UTF-8 support in window title. - C.XSetTextProperty(dpy, win, - &C.XTextProperty{ - value: (*C.uchar)(unsafe.Pointer(ctitle)), - encoding: w.atoms.utf8string, - format: 8, - nitems: C.ulong(len(opts.Title)), - }, - w.atom("_NET_WM_NAME", false)) + w.atoms.wmName = w.atom("_NET_WM_NAME", false) + w.atoms.wmState = w.atom("_NET_WM_STATE", false) + w.atoms.wmStateFullscreen = w.atom("_NET_WM_STATE_FULLSCREEN", false) + w.atoms.wmActiveWindow = w.atom("_NET_ACTIVE_WINDOW", false) + w.atoms.wmStateMaximizedHorz = w.atom("_NET_WM_STATE_MAXIMIZED_HORZ", false) + w.atoms.wmStateMaximizedVert = w.atom("_NET_WM_STATE_MAXIMIZED_VERT", false) // extensions C.XSetWMProtocols(dpy, win, &w.atoms.evDelWindow, 1) - // make the window visible on the screen - C.XMapWindow(dpy, win) - go func() { w.w.SetDriver(w) + w.Configure(options) + + // make the window visible on the screen + C.XMapWindow(dpy, win) + w.w.Event(ViewEvent{Display: unsafe.Pointer(dpy), Window: uintptr(win)}) w.setStage(system.StageRunning) w.loop() + w.w.Event(ViewEvent{}) w.destroy() }() return nil diff --git a/vendor/gioui.org/app/internal/window/runmain.go b/vendor/gioui.org/app/runmain.go index f9de5d0..a1c1e3d 100644 --- a/vendor/gioui.org/app/internal/window/runmain.go +++ b/vendor/gioui.org/app/runmain.go @@ -1,8 +1,9 @@ // SPDX-License-Identifier: Unlicense OR MIT +//go:build android || (darwin && ios) // +build android darwin,ios -package window +package app // Android only supports non-Java programs as c-shared libraries. // Unfortunately, Go does not run a program's main function in diff --git a/vendor/gioui.org/app/sigpipe_darwin.go b/vendor/gioui.org/app/sigpipe_darwin.go deleted file mode 100644 index aca19b7..0000000 --- a/vendor/gioui.org/app/sigpipe_darwin.go +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: Unlicense OR MIT - -// +build !go1.14 - -// Work around golang.org/issue/33384, fixed in CL 191785, -// to be released in Go 1.14. - -package app - -import ( - "os" - "os/signal" - "syscall" -) - -func init() { - signal.Notify(make(chan os.Signal), syscall.SIGPIPE) -} diff --git a/vendor/gioui.org/app/vulkan.go b/vendor/gioui.org/app/vulkan.go new file mode 100644 index 0000000..630c254 --- /dev/null +++ b/vendor/gioui.org/app/vulkan.go @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build (linux || freebsd) && !novulkan +// +build linux freebsd +// +build !novulkan + +package app + +import ( + "errors" + "unsafe" + + "gioui.org/gpu" + "gioui.org/internal/vk" +) + +type vkContext struct { + physDev vk.PhysicalDevice + inst vk.Instance + dev vk.Device + queueFam int + queue vk.Queue + acquireSem vk.Semaphore + presentSem vk.Semaphore + + swchain vk.Swapchain + imgs []vk.Image + views []vk.ImageView + fbos []vk.Framebuffer + format vk.Format + presentIdx int +} + +func newVulkanContext(inst vk.Instance, surf vk.Surface) (*vkContext, error) { + physDev, qFam, err := vk.ChoosePhysicalDevice(inst, surf) + if err != nil { + return nil, err + } + dev, err := vk.CreateDeviceAndQueue(physDev, qFam, "VK_KHR_swapchain") + if err != nil { + return nil, err + } + if err != nil { + vk.DestroyDevice(dev) + return nil, err + } + acquireSem, err := vk.CreateSemaphore(dev) + if err != nil { + vk.DestroyDevice(dev) + return nil, err + } + presentSem, err := vk.CreateSemaphore(dev) + if err != nil { + vk.DestroySemaphore(dev, acquireSem) + vk.DestroyDevice(dev) + return nil, err + } + c := &vkContext{ + physDev: physDev, + inst: inst, + dev: dev, + queueFam: qFam, + queue: vk.GetDeviceQueue(dev, qFam, 0), + acquireSem: acquireSem, + presentSem: presentSem, + } + return c, nil +} + +func (c *vkContext) RenderTarget() (gpu.RenderTarget, error) { + vk.DeviceWaitIdle(c.dev) + + imgIdx, err := vk.AcquireNextImage(c.dev, c.swchain, c.acquireSem, 0) + if err := mapSurfaceErr(err); err != nil { + return nil, err + } + c.presentIdx = imgIdx + return gpu.VulkanRenderTarget{ + WaitSem: uint64(c.acquireSem), + SignalSem: uint64(c.presentSem), + Framebuffer: uint64(c.fbos[imgIdx]), + Image: uint64(c.imgs[imgIdx]), + }, nil +} + +func (c *vkContext) api() gpu.API { + return gpu.Vulkan{ + PhysDevice: unsafe.Pointer(c.physDev), + Device: unsafe.Pointer(c.dev), + Format: int(c.format), + QueueFamily: c.queueFam, + QueueIndex: 0, + } +} + +func mapErr(err error) error { + var vkErr vk.Error + if errors.As(err, &vkErr) && vkErr == vk.ERROR_DEVICE_LOST { + return gpu.ErrDeviceLost + } + return err +} + +func mapSurfaceErr(err error) error { + var vkErr vk.Error + if !errors.As(err, &vkErr) { + return err + } + switch { + case vkErr == vk.SUBOPTIMAL_KHR: + // Android reports VK_SUBOPTIMAL_KHR when presenting to a rotated + // swapchain (preTransform != currentTransform). However, we don't + // support transforming the output ourselves, so we'll live with it. + return nil + case vkErr == vk.ERROR_OUT_OF_DATE_KHR: + return errOutOfDate + case vkErr == vk.ERROR_SURFACE_LOST_KHR: + // Treating a lost surface as a lost device isn't accurate, but + // probably not worth optimizing. + return gpu.ErrDeviceLost + } + return mapErr(err) +} + +func (c *vkContext) release() { + vk.DeviceWaitIdle(c.dev) + + c.destroySwapchain() + vk.DestroySemaphore(c.dev, c.acquireSem) + vk.DestroySemaphore(c.dev, c.presentSem) + vk.DestroyDevice(c.dev) + *c = vkContext{} +} + +func (c *vkContext) present() error { + return mapSurfaceErr(vk.PresentQueue(c.queue, c.swchain, c.presentSem, c.presentIdx)) +} + +func (c *vkContext) destroyImageViews() { + for _, f := range c.fbos { + vk.DestroyFramebuffer(c.dev, f) + } + c.fbos = nil + for _, view := range c.views { + vk.DestroyImageView(c.dev, view) + } + c.views = nil +} + +func (c *vkContext) destroySwapchain() { + vk.DeviceWaitIdle(c.dev) + + c.destroyImageViews() + if c.swchain != 0 { + vk.DestroySwapchain(c.dev, c.swchain) + c.swchain = 0 + } +} + +func (c *vkContext) refresh(surf vk.Surface, width, height int) error { + vk.DeviceWaitIdle(c.dev) + + c.destroyImageViews() + // Check whether size is valid. That's needed on X11, where ConfigureNotify + // is not always synchronized with the window extent. + caps, err := vk.GetPhysicalDeviceSurfaceCapabilities(c.physDev, surf) + if err != nil { + return err + } + minExt, maxExt := caps.MinExtent(), caps.MaxExtent() + if width < minExt.X || maxExt.X < width || height < minExt.Y || maxExt.Y < height { + return errOutOfDate + } + swchain, imgs, format, err := vk.CreateSwapchain(c.physDev, c.dev, surf, width, height, c.swchain) + if c.swchain != 0 { + vk.DestroySwapchain(c.dev, c.swchain) + c.swchain = 0 + } + if err := mapSurfaceErr(err); err != nil { + return err + } + c.swchain = swchain + c.imgs = imgs + c.format = format + pass, err := vk.CreateRenderPass( + c.dev, + format, + vk.ATTACHMENT_LOAD_OP_CLEAR, + vk.IMAGE_LAYOUT_UNDEFINED, + vk.IMAGE_LAYOUT_PRESENT_SRC_KHR, + nil, + ) + if err := mapErr(err); err != nil { + return err + } + defer vk.DestroyRenderPass(c.dev, pass) + for _, img := range imgs { + view, err := vk.CreateImageView(c.dev, img, format) + if err := mapErr(err); err != nil { + return err + } + c.views = append(c.views, view) + fbo, err := vk.CreateFramebuffer(c.dev, pass, view, width, height) + if err := mapErr(err); err != nil { + return err + } + c.fbos = append(c.fbos, fbo) + } + return nil +} diff --git a/vendor/gioui.org/app/vulkan_android.go b/vendor/gioui.org/app/vulkan_android.go new file mode 100644 index 0000000..3758d04 --- /dev/null +++ b/vendor/gioui.org/app/vulkan_android.go @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build !novulkan +// +build !novulkan + +package app + +import ( + "unsafe" + + "gioui.org/gpu" + "gioui.org/internal/vk" +) + +type wlVkContext struct { + win *window + inst vk.Instance + surf vk.Surface + ctx *vkContext +} + +func init() { + newAndroidVulkanContext = func(w *window) (context, error) { + inst, err := vk.CreateInstance("VK_KHR_surface", "VK_KHR_android_surface") + if err != nil { + return nil, err + } + window, _, _ := w.nativeWindow() + surf, err := vk.CreateAndroidSurface(inst, unsafe.Pointer(window)) + if err != nil { + vk.DestroyInstance(inst) + return nil, err + } + ctx, err := newVulkanContext(inst, surf) + if err != nil { + vk.DestroySurface(inst, surf) + vk.DestroyInstance(inst) + return nil, err + } + c := &wlVkContext{ + win: w, + inst: inst, + surf: surf, + ctx: ctx, + } + return c, nil + } +} + +func (c *wlVkContext) RenderTarget() (gpu.RenderTarget, error) { + return c.ctx.RenderTarget() +} + +func (c *wlVkContext) API() gpu.API { + return c.ctx.api() +} + +func (c *wlVkContext) Release() { + c.ctx.release() + if c.surf != 0 { + vk.DestroySurface(c.inst, c.surf) + } + vk.DestroyInstance(c.inst) + *c = wlVkContext{} +} + +func (c *wlVkContext) Present() error { + return c.ctx.present() +} + +func (c *wlVkContext) Lock() error { + return nil +} + +func (c *wlVkContext) Unlock() {} + +func (c *wlVkContext) Refresh() error { + win, w, h := c.win.nativeWindow() + if c.surf != 0 { + c.ctx.destroySwapchain() + vk.DestroySurface(c.inst, c.surf) + c.surf = 0 + } + surf, err := vk.CreateAndroidSurface(c.inst, unsafe.Pointer(win)) + if err != nil { + return err + } + c.surf = surf + return c.ctx.refresh(c.surf, w, h) +} diff --git a/vendor/gioui.org/app/vulkan_wayland.go b/vendor/gioui.org/app/vulkan_wayland.go new file mode 100644 index 0000000..7bf0662 --- /dev/null +++ b/vendor/gioui.org/app/vulkan_wayland.go @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build ((linux && !android) || freebsd) && !nowayland && !novulkan +// +build linux,!android freebsd +// +build !nowayland +// +build !novulkan + +package app + +import ( + "unsafe" + + "gioui.org/gpu" + "gioui.org/internal/vk" +) + +type wlVkContext struct { + win *window + inst vk.Instance + surf vk.Surface + ctx *vkContext +} + +func init() { + newWaylandVulkanContext = func(w *window) (context, error) { + inst, err := vk.CreateInstance("VK_KHR_surface", "VK_KHR_wayland_surface") + if err != nil { + return nil, err + } + disp := w.display() + wlSurf, _, _ := w.surface() + surf, err := vk.CreateWaylandSurface(inst, unsafe.Pointer(disp), unsafe.Pointer(wlSurf)) + if err != nil { + vk.DestroyInstance(inst) + return nil, err + } + ctx, err := newVulkanContext(inst, surf) + if err != nil { + vk.DestroySurface(inst, surf) + vk.DestroyInstance(inst) + return nil, err + } + c := &wlVkContext{ + win: w, + inst: inst, + surf: surf, + ctx: ctx, + } + return c, nil + } +} + +func (c *wlVkContext) RenderTarget() (gpu.RenderTarget, error) { + return c.ctx.RenderTarget() +} + +func (c *wlVkContext) API() gpu.API { + return c.ctx.api() +} + +func (c *wlVkContext) Release() { + c.ctx.release() + vk.DestroySurface(c.inst, c.surf) + vk.DestroyInstance(c.inst) + *c = wlVkContext{} +} + +func (c *wlVkContext) Present() error { + return c.ctx.present() +} + +func (c *wlVkContext) Lock() error { + return nil +} + +func (c *wlVkContext) Unlock() {} + +func (c *wlVkContext) Refresh() error { + _, w, h := c.win.surface() + return c.ctx.refresh(c.surf, w, h) +} diff --git a/vendor/gioui.org/app/vulkan_x11.go b/vendor/gioui.org/app/vulkan_x11.go new file mode 100644 index 0000000..cf97774 --- /dev/null +++ b/vendor/gioui.org/app/vulkan_x11.go @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build ((linux && !android) || freebsd) && !nox11 && !novulkan +// +build linux,!android freebsd +// +build !nox11 +// +build !novulkan + +package app + +import ( + "unsafe" + + "gioui.org/gpu" + "gioui.org/internal/vk" +) + +type x11VkContext struct { + win *x11Window + inst vk.Instance + surf vk.Surface + ctx *vkContext +} + +func init() { + newX11VulkanContext = func(w *x11Window) (context, error) { + inst, err := vk.CreateInstance("VK_KHR_surface", "VK_KHR_xlib_surface") + if err != nil { + return nil, err + } + disp := w.display() + window, _, _ := w.window() + surf, err := vk.CreateXlibSurface(inst, unsafe.Pointer(disp), uintptr(window)) + if err != nil { + vk.DestroyInstance(inst) + return nil, err + } + ctx, err := newVulkanContext(inst, surf) + if err != nil { + vk.DestroySurface(inst, surf) + vk.DestroyInstance(inst) + return nil, err + } + c := &x11VkContext{ + win: w, + inst: inst, + surf: surf, + ctx: ctx, + } + return c, nil + } +} + +func (c *x11VkContext) RenderTarget() (gpu.RenderTarget, error) { + return c.ctx.RenderTarget() +} + +func (c *x11VkContext) API() gpu.API { + return c.ctx.api() +} + +func (c *x11VkContext) Release() { + c.ctx.release() + vk.DestroySurface(c.inst, c.surf) + vk.DestroyInstance(c.inst) + *c = x11VkContext{} +} + +func (c *x11VkContext) Present() error { + return c.ctx.present() +} + +func (c *x11VkContext) Lock() error { + return nil +} + +func (c *x11VkContext) Unlock() {} + +func (c *x11VkContext) Refresh() error { + _, w, h := c.win.window() + return c.ctx.refresh(c.surf, w, h) +} diff --git a/vendor/gioui.org/app/internal/window/wayland_text_input.c b/vendor/gioui.org/app/wayland_text_input.c index a69af09..65de0bb 100644 --- a/vendor/gioui.org/app/internal/window/wayland_text_input.c +++ b/vendor/gioui.org/app/wayland_text_input.c @@ -1,6 +1,8 @@ -// +build linux,!android,!nowayland freebsd +//go:build ((linux && !android) || freebsd) && !nowayland +// +build linux,!android freebsd +// +build !nowayland -/* Generated by wayland-scanner 1.17.0 */ +/* Generated by wayland-scanner 1.19.0 */ /* * Copyright © 2012, 2013 Intel Corporation @@ -48,7 +50,7 @@ extern const struct wl_interface wl_seat_interface; extern const struct wl_interface wl_surface_interface; extern const struct wl_interface zwp_text_input_v3_interface; -static const struct wl_interface *types[] = { +static const struct wl_interface *text_input_unstable_v3_types[] = { NULL, NULL, NULL, @@ -60,23 +62,23 @@ static const struct wl_interface *types[] = { }; static const struct wl_message zwp_text_input_v3_requests[] = { - { "destroy", "", types + 0 }, - { "enable", "", types + 0 }, - { "disable", "", types + 0 }, - { "set_surrounding_text", "sii", types + 0 }, - { "set_text_change_cause", "u", types + 0 }, - { "set_content_type", "uu", types + 0 }, - { "set_cursor_rectangle", "iiii", types + 0 }, - { "commit", "", types + 0 }, + { "destroy", "", text_input_unstable_v3_types + 0 }, + { "enable", "", text_input_unstable_v3_types + 0 }, + { "disable", "", text_input_unstable_v3_types + 0 }, + { "set_surrounding_text", "sii", text_input_unstable_v3_types + 0 }, + { "set_text_change_cause", "u", text_input_unstable_v3_types + 0 }, + { "set_content_type", "uu", text_input_unstable_v3_types + 0 }, + { "set_cursor_rectangle", "iiii", text_input_unstable_v3_types + 0 }, + { "commit", "", text_input_unstable_v3_types + 0 }, }; static const struct wl_message zwp_text_input_v3_events[] = { - { "enter", "o", types + 4 }, - { "leave", "o", types + 5 }, - { "preedit_string", "?sii", types + 0 }, - { "commit_string", "?s", types + 0 }, - { "delete_surrounding_text", "uu", types + 0 }, - { "done", "u", types + 0 }, + { "enter", "o", text_input_unstable_v3_types + 4 }, + { "leave", "o", text_input_unstable_v3_types + 5 }, + { "preedit_string", "?sii", text_input_unstable_v3_types + 0 }, + { "commit_string", "?s", text_input_unstable_v3_types + 0 }, + { "delete_surrounding_text", "uu", text_input_unstable_v3_types + 0 }, + { "done", "u", text_input_unstable_v3_types + 0 }, }; WL_PRIVATE const struct wl_interface zwp_text_input_v3_interface = { @@ -86,8 +88,8 @@ WL_PRIVATE const struct wl_interface zwp_text_input_v3_interface = { }; static const struct wl_message zwp_text_input_manager_v3_requests[] = { - { "destroy", "", types + 0 }, - { "get_text_input", "no", types + 6 }, + { "destroy", "", text_input_unstable_v3_types + 0 }, + { "get_text_input", "no", text_input_unstable_v3_types + 6 }, }; WL_PRIVATE const struct wl_interface zwp_text_input_manager_v3_interface = { diff --git a/vendor/gioui.org/app/internal/window/wayland_text_input.h b/vendor/gioui.org/app/wayland_text_input.h index 8a6f8dd..882da43 100644 --- a/vendor/gioui.org/app/internal/window/wayland_text_input.h +++ b/vendor/gioui.org/app/wayland_text_input.h @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.17.0 */ +/* Generated by wayland-scanner 1.19.0 */ #ifndef TEXT_INPUT_UNSTABLE_V3_CLIENT_PROTOCOL_H #define TEXT_INPUT_UNSTABLE_V3_CLIENT_PROTOCOL_H @@ -71,6 +71,8 @@ struct wl_surface; struct zwp_text_input_manager_v3; struct zwp_text_input_v3; +#ifndef ZWP_TEXT_INPUT_V3_INTERFACE +#define ZWP_TEXT_INPUT_V3_INTERFACE /** * @page page_iface_zwp_text_input_v3 zwp_text_input_v3 * @section page_iface_zwp_text_input_v3_desc Description @@ -135,6 +137,9 @@ struct zwp_text_input_v3; * needs to be resent by the client. */ extern const struct wl_interface zwp_text_input_v3_interface; +#endif +#ifndef ZWP_TEXT_INPUT_MANAGER_V3_INTERFACE +#define ZWP_TEXT_INPUT_MANAGER_V3_INTERFACE /** * @page page_iface_zwp_text_input_manager_v3 zwp_text_input_manager_v3 * @section page_iface_zwp_text_input_manager_v3_desc Description @@ -149,6 +154,7 @@ extern const struct wl_interface zwp_text_input_v3_interface; * A factory for text-input objects. This object is a global singleton. */ extern const struct wl_interface zwp_text_input_manager_v3_interface; +#endif #ifndef ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_ENUM #define ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_ENUM @@ -310,6 +316,9 @@ struct zwp_text_input_v3_listener { * Notification that this seat's text-input focus is on a certain * surface. * + * If client has created multiple text input objects, compositor + * must send this event to all of them. + * * When the seat has the keyboard capability the text-input focus * follows the keyboard focus. This event sets the current surface * for the text-input object. @@ -325,7 +334,9 @@ struct zwp_text_input_v3_listener { * previously set. * * The leave notification clears the current surface. It is sent - * before the enter notification for the new focus. + * before the enter notification for the new focus. After leave + * event, compositor must ignore requests from any text input + * instances until next enter event. * * When the seat has the keyboard capability the text-input focus * follows the keyboard focus. @@ -559,6 +570,12 @@ zwp_text_input_v3_destroy(struct zwp_text_input_v3 *zwp_text_input_v3) * zwp_text_input_v3.disable when there is no longer any input focus on * the current surface. * + * Clients must not enable more than one text input on the single seat + * and should disable the current text input before enabling the new one. + * At most one instance of text input may be in enabled state per instance, + * Requests to enable the another text input when some text input is active + * must be ignored by compositor. + * * This request resets all state associated with previous enable, disable, * set_surrounding_text, set_text_change_cause, set_content_type, and * set_cursor_rectangle requests, as well as the state associated with diff --git a/vendor/gioui.org/app/internal/window/wayland_xdg_decoration.c b/vendor/gioui.org/app/wayland_xdg_decoration.c index fa13d7c..ee94c60 100644 --- a/vendor/gioui.org/app/internal/window/wayland_xdg_decoration.c +++ b/vendor/gioui.org/app/wayland_xdg_decoration.c @@ -1,6 +1,8 @@ -// +build linux,!android,!nowayland freebsd +//go:build ((linux && !android) || freebsd) && !nowayland +// +build linux,!android freebsd +// +build !nowayland -/* Generated by wayland-scanner 1.17.0 */ +/* Generated by wayland-scanner 1.19.0 */ /* * Copyright © 2018 Simon Ser @@ -42,15 +44,15 @@ extern const struct wl_interface xdg_toplevel_interface; extern const struct wl_interface zxdg_toplevel_decoration_v1_interface; -static const struct wl_interface *types[] = { +static const struct wl_interface *xdg_decoration_unstable_v1_types[] = { NULL, &zxdg_toplevel_decoration_v1_interface, &xdg_toplevel_interface, }; static const struct wl_message zxdg_decoration_manager_v1_requests[] = { - { "destroy", "", types + 0 }, - { "get_toplevel_decoration", "no", types + 1 }, + { "destroy", "", xdg_decoration_unstable_v1_types + 0 }, + { "get_toplevel_decoration", "no", xdg_decoration_unstable_v1_types + 1 }, }; WL_PRIVATE const struct wl_interface zxdg_decoration_manager_v1_interface = { @@ -60,13 +62,13 @@ WL_PRIVATE const struct wl_interface zxdg_decoration_manager_v1_interface = { }; static const struct wl_message zxdg_toplevel_decoration_v1_requests[] = { - { "destroy", "", types + 0 }, - { "set_mode", "u", types + 0 }, - { "unset_mode", "", types + 0 }, + { "destroy", "", xdg_decoration_unstable_v1_types + 0 }, + { "set_mode", "u", xdg_decoration_unstable_v1_types + 0 }, + { "unset_mode", "", xdg_decoration_unstable_v1_types + 0 }, }; static const struct wl_message zxdg_toplevel_decoration_v1_events[] = { - { "configure", "u", types + 0 }, + { "configure", "u", xdg_decoration_unstable_v1_types + 0 }, }; WL_PRIVATE const struct wl_interface zxdg_toplevel_decoration_v1_interface = { diff --git a/vendor/gioui.org/app/internal/window/wayland_xdg_decoration.h b/vendor/gioui.org/app/wayland_xdg_decoration.h index 044ea2e..004d342 100644 --- a/vendor/gioui.org/app/internal/window/wayland_xdg_decoration.h +++ b/vendor/gioui.org/app/wayland_xdg_decoration.h @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.17.0 */ +/* Generated by wayland-scanner 1.19.0 */ #ifndef XDG_DECORATION_UNSTABLE_V1_CLIENT_PROTOCOL_H #define XDG_DECORATION_UNSTABLE_V1_CLIENT_PROTOCOL_H @@ -45,6 +45,8 @@ struct xdg_toplevel; struct zxdg_decoration_manager_v1; struct zxdg_toplevel_decoration_v1; +#ifndef ZXDG_DECORATION_MANAGER_V1_INTERFACE +#define ZXDG_DECORATION_MANAGER_V1_INTERFACE /** * @page page_iface_zxdg_decoration_manager_v1 zxdg_decoration_manager_v1 * @section page_iface_zxdg_decoration_manager_v1_desc Description @@ -101,6 +103,9 @@ struct zxdg_toplevel_decoration_v1; * interface version number is reset. */ extern const struct wl_interface zxdg_decoration_manager_v1_interface; +#endif +#ifndef ZXDG_TOPLEVEL_DECORATION_V1_INTERFACE +#define ZXDG_TOPLEVEL_DECORATION_V1_INTERFACE /** * @page page_iface_zxdg_toplevel_decoration_v1 zxdg_toplevel_decoration_v1 * @section page_iface_zxdg_toplevel_decoration_v1_desc Description @@ -125,6 +130,7 @@ extern const struct wl_interface zxdg_decoration_manager_v1_interface; * xdg_toplevel. */ extern const struct wl_interface zxdg_toplevel_decoration_v1_interface; +#endif #define ZXDG_DECORATION_MANAGER_V1_DESTROY 0 #define ZXDG_DECORATION_MANAGER_V1_GET_TOPLEVEL_DECORATION 1 @@ -332,7 +338,7 @@ zxdg_toplevel_decoration_v1_destroy(struct zxdg_toplevel_decoration_v1 *zxdg_top * that the client prefers the provided decoration mode. * * After requesting a decoration mode, the compositor will respond by - * emitting a xdg_surface.configure event. The client should then update + * emitting an xdg_surface.configure event. The client should then update * its content, drawing it without decorations if the received mode is * server-side decorations. The client must also acknowledge the configure * when committing the new content (see xdg_surface.ack_configure). @@ -341,7 +347,7 @@ zxdg_toplevel_decoration_v1_destroy(struct zxdg_toplevel_decoration_v1 *zxdg_top * different mode instead. * * Clients whose decoration mode depend on the xdg_toplevel state may send - * a set_mode request in response to a xdg_surface.configure event and wait + * a set_mode request in response to an xdg_surface.configure event and wait * for the next xdg_surface.configure event to prevent unwanted state. * Such clients are responsible for preventing configure loops and must * make sure not to send multiple successive set_mode requests with the diff --git a/vendor/gioui.org/app/internal/window/wayland_xdg_shell.c b/vendor/gioui.org/app/wayland_xdg_shell.c index 1947847..54fe5cf 100644 --- a/vendor/gioui.org/app/internal/window/wayland_xdg_shell.c +++ b/vendor/gioui.org/app/wayland_xdg_shell.c @@ -1,6 +1,8 @@ -// +build linux,!android,!nowayland freebsd +//go:build ((linux && !android) || freebsd) && !nowayland +// +build linux,!android freebsd +// +build !nowayland -/* Generated by wayland-scanner 1.17.0 */ +/* Generated by wayland-scanner 1.19.0 */ /* * Copyright © 2008-2013 Kristian Høgsberg @@ -52,7 +54,7 @@ extern const struct wl_interface xdg_positioner_interface; extern const struct wl_interface xdg_surface_interface; extern const struct wl_interface xdg_toplevel_interface; -static const struct wl_interface *types[] = { +static const struct wl_interface *xdg_shell_types[] = { NULL, NULL, NULL, @@ -77,100 +79,107 @@ static const struct wl_interface *types[] = { &wl_output_interface, &wl_seat_interface, NULL, + &xdg_positioner_interface, + NULL, }; static const struct wl_message xdg_wm_base_requests[] = { - { "destroy", "", types + 0 }, - { "create_positioner", "n", types + 4 }, - { "get_xdg_surface", "no", types + 5 }, - { "pong", "u", types + 0 }, + { "destroy", "", xdg_shell_types + 0 }, + { "create_positioner", "n", xdg_shell_types + 4 }, + { "get_xdg_surface", "no", xdg_shell_types + 5 }, + { "pong", "u", xdg_shell_types + 0 }, }; static const struct wl_message xdg_wm_base_events[] = { - { "ping", "u", types + 0 }, + { "ping", "u", xdg_shell_types + 0 }, }; WL_PRIVATE const struct wl_interface xdg_wm_base_interface = { - "xdg_wm_base", 2, + "xdg_wm_base", 3, 4, xdg_wm_base_requests, 1, xdg_wm_base_events, }; static const struct wl_message xdg_positioner_requests[] = { - { "destroy", "", types + 0 }, - { "set_size", "ii", types + 0 }, - { "set_anchor_rect", "iiii", types + 0 }, - { "set_anchor", "u", types + 0 }, - { "set_gravity", "u", types + 0 }, - { "set_constraint_adjustment", "u", types + 0 }, - { "set_offset", "ii", types + 0 }, + { "destroy", "", xdg_shell_types + 0 }, + { "set_size", "ii", xdg_shell_types + 0 }, + { "set_anchor_rect", "iiii", xdg_shell_types + 0 }, + { "set_anchor", "u", xdg_shell_types + 0 }, + { "set_gravity", "u", xdg_shell_types + 0 }, + { "set_constraint_adjustment", "u", xdg_shell_types + 0 }, + { "set_offset", "ii", xdg_shell_types + 0 }, + { "set_reactive", "3", xdg_shell_types + 0 }, + { "set_parent_size", "3ii", xdg_shell_types + 0 }, + { "set_parent_configure", "3u", xdg_shell_types + 0 }, }; WL_PRIVATE const struct wl_interface xdg_positioner_interface = { - "xdg_positioner", 2, - 7, xdg_positioner_requests, + "xdg_positioner", 3, + 10, xdg_positioner_requests, 0, NULL, }; static const struct wl_message xdg_surface_requests[] = { - { "destroy", "", types + 0 }, - { "get_toplevel", "n", types + 7 }, - { "get_popup", "n?oo", types + 8 }, - { "set_window_geometry", "iiii", types + 0 }, - { "ack_configure", "u", types + 0 }, + { "destroy", "", xdg_shell_types + 0 }, + { "get_toplevel", "n", xdg_shell_types + 7 }, + { "get_popup", "n?oo", xdg_shell_types + 8 }, + { "set_window_geometry", "iiii", xdg_shell_types + 0 }, + { "ack_configure", "u", xdg_shell_types + 0 }, }; static const struct wl_message xdg_surface_events[] = { - { "configure", "u", types + 0 }, + { "configure", "u", xdg_shell_types + 0 }, }; WL_PRIVATE const struct wl_interface xdg_surface_interface = { - "xdg_surface", 2, + "xdg_surface", 3, 5, xdg_surface_requests, 1, xdg_surface_events, }; static const struct wl_message xdg_toplevel_requests[] = { - { "destroy", "", types + 0 }, - { "set_parent", "?o", types + 11 }, - { "set_title", "s", types + 0 }, - { "set_app_id", "s", types + 0 }, - { "show_window_menu", "ouii", types + 12 }, - { "move", "ou", types + 16 }, - { "resize", "ouu", types + 18 }, - { "set_max_size", "ii", types + 0 }, - { "set_min_size", "ii", types + 0 }, - { "set_maximized", "", types + 0 }, - { "unset_maximized", "", types + 0 }, - { "set_fullscreen", "?o", types + 21 }, - { "unset_fullscreen", "", types + 0 }, - { "set_minimized", "", types + 0 }, + { "destroy", "", xdg_shell_types + 0 }, + { "set_parent", "?o", xdg_shell_types + 11 }, + { "set_title", "s", xdg_shell_types + 0 }, + { "set_app_id", "s", xdg_shell_types + 0 }, + { "show_window_menu", "ouii", xdg_shell_types + 12 }, + { "move", "ou", xdg_shell_types + 16 }, + { "resize", "ouu", xdg_shell_types + 18 }, + { "set_max_size", "ii", xdg_shell_types + 0 }, + { "set_min_size", "ii", xdg_shell_types + 0 }, + { "set_maximized", "", xdg_shell_types + 0 }, + { "unset_maximized", "", xdg_shell_types + 0 }, + { "set_fullscreen", "?o", xdg_shell_types + 21 }, + { "unset_fullscreen", "", xdg_shell_types + 0 }, + { "set_minimized", "", xdg_shell_types + 0 }, }; static const struct wl_message xdg_toplevel_events[] = { - { "configure", "iia", types + 0 }, - { "close", "", types + 0 }, + { "configure", "iia", xdg_shell_types + 0 }, + { "close", "", xdg_shell_types + 0 }, }; WL_PRIVATE const struct wl_interface xdg_toplevel_interface = { - "xdg_toplevel", 2, + "xdg_toplevel", 3, 14, xdg_toplevel_requests, 2, xdg_toplevel_events, }; static const struct wl_message xdg_popup_requests[] = { - { "destroy", "", types + 0 }, - { "grab", "ou", types + 22 }, + { "destroy", "", xdg_shell_types + 0 }, + { "grab", "ou", xdg_shell_types + 22 }, + { "reposition", "3ou", xdg_shell_types + 24 }, }; static const struct wl_message xdg_popup_events[] = { - { "configure", "iiii", types + 0 }, - { "popup_done", "", types + 0 }, + { "configure", "iiii", xdg_shell_types + 0 }, + { "popup_done", "", xdg_shell_types + 0 }, + { "repositioned", "3u", xdg_shell_types + 0 }, }; WL_PRIVATE const struct wl_interface xdg_popup_interface = { - "xdg_popup", 2, - 2, xdg_popup_requests, - 2, xdg_popup_events, + "xdg_popup", 3, + 3, xdg_popup_requests, + 3, xdg_popup_events, }; diff --git a/vendor/gioui.org/app/internal/window/wayland_xdg_shell.h b/vendor/gioui.org/app/wayland_xdg_shell.h index 1f4bfb5..1db8fd9 100644 --- a/vendor/gioui.org/app/internal/window/wayland_xdg_shell.h +++ b/vendor/gioui.org/app/wayland_xdg_shell.h @@ -1,4 +1,4 @@ -/* Generated by wayland-scanner 1.17.0 */ +/* Generated by wayland-scanner 1.19.0 */ #ifndef XDG_SHELL_CLIENT_PROTOCOL_H #define XDG_SHELL_CLIENT_PROTOCOL_H @@ -58,6 +58,8 @@ struct xdg_surface; struct xdg_toplevel; struct xdg_wm_base; +#ifndef XDG_WM_BASE_INTERFACE +#define XDG_WM_BASE_INTERFACE /** * @page page_iface_xdg_wm_base xdg_wm_base * @section page_iface_xdg_wm_base_desc Description @@ -80,6 +82,9 @@ struct xdg_wm_base; * creating transient windows such as popup menus. */ extern const struct wl_interface xdg_wm_base_interface; +#endif +#ifndef XDG_POSITIONER_INTERFACE +#define XDG_POSITIONER_INTERFACE /** * @page page_iface_xdg_positioner xdg_positioner * @section page_iface_xdg_positioner_desc Description @@ -130,6 +135,9 @@ extern const struct wl_interface xdg_wm_base_interface; * positioning a surface raises an error. */ extern const struct wl_interface xdg_positioner_interface; +#endif +#ifndef XDG_SURFACE_INTERFACE +#define XDG_SURFACE_INTERFACE /** * @page page_iface_xdg_surface xdg_surface * @section page_iface_xdg_surface_desc Description @@ -159,6 +167,11 @@ extern const struct wl_interface xdg_positioner_interface; * manipulate a buffer prior to the first xdg_surface.configure call must * also be treated as errors. * + * After creating a role-specific object and setting it up, the client must + * perform an initial commit without any buffer attached. The compositor + * will reply with an xdg_surface.configure event. The client must + * acknowledge it and is then allowed to attach a buffer to map the surface. + * * Mapping an xdg_surface-based role surface is defined as making it * possible for the surface to be shown by the compositor. Note that * a mapped surface is not guaranteed to be visible once it is mapped. @@ -204,6 +217,11 @@ extern const struct wl_interface xdg_positioner_interface; * manipulate a buffer prior to the first xdg_surface.configure call must * also be treated as errors. * + * After creating a role-specific object and setting it up, the client must + * perform an initial commit without any buffer attached. The compositor + * will reply with an xdg_surface.configure event. The client must + * acknowledge it and is then allowed to attach a buffer to map the surface. + * * Mapping an xdg_surface-based role surface is defined as making it * possible for the surface to be shown by the compositor. Note that * a mapped surface is not guaranteed to be visible once it is mapped. @@ -220,6 +238,9 @@ extern const struct wl_interface xdg_positioner_interface; * has not been destroyed. */ extern const struct wl_interface xdg_surface_interface; +#endif +#ifndef XDG_TOPLEVEL_INTERFACE +#define XDG_TOPLEVEL_INTERFACE /** * @page page_iface_xdg_toplevel xdg_toplevel * @section page_iface_xdg_toplevel_desc Description @@ -234,7 +255,11 @@ extern const struct wl_interface xdg_surface_interface; * by the compositor until it is explicitly mapped again. * All active operations (e.g., move, resize) are canceled and all * attributes (e.g. title, state, stacking, ...) are discarded for - * an xdg_toplevel surface when it is unmapped. + * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to + * the state it had right after xdg_surface.get_toplevel. The client + * can re-map the toplevel by perfoming a commit without any buffer + * attached, waiting for a configure event and handling it as usual (see + * xdg_surface description). * * Attaching a null buffer to a toplevel unmaps the surface. * @section page_iface_xdg_toplevel_api API @@ -253,11 +278,18 @@ extern const struct wl_interface xdg_surface_interface; * by the compositor until it is explicitly mapped again. * All active operations (e.g., move, resize) are canceled and all * attributes (e.g. title, state, stacking, ...) are discarded for - * an xdg_toplevel surface when it is unmapped. + * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to + * the state it had right after xdg_surface.get_toplevel. The client + * can re-map the toplevel by perfoming a commit without any buffer + * attached, waiting for a configure event and handling it as usual (see + * xdg_surface description). * * Attaching a null buffer to a toplevel unmaps the surface. */ extern const struct wl_interface xdg_toplevel_interface; +#endif +#ifndef XDG_POPUP_INTERFACE +#define XDG_POPUP_INTERFACE /** * @page page_iface_xdg_popup xdg_popup * @section page_iface_xdg_popup_desc Description @@ -284,12 +316,6 @@ extern const struct wl_interface xdg_toplevel_interface; * The parent of an xdg_popup must be mapped (see the xdg_surface * description) before the xdg_popup itself. * - * The x and y arguments passed when creating the popup object specify - * where the top left of the popup should be placed, relative to the - * local surface coordinates of the parent surface. See - * xdg_surface.get_popup. An xdg_popup must intersect with or be at least - * partially adjacent to its parent surface. - * * The client must call wl_surface.commit on the corresponding wl_surface * for the xdg_popup state to take effect. * @section page_iface_xdg_popup_api API @@ -320,16 +346,11 @@ extern const struct wl_interface xdg_toplevel_interface; * The parent of an xdg_popup must be mapped (see the xdg_surface * description) before the xdg_popup itself. * - * The x and y arguments passed when creating the popup object specify - * where the top left of the popup should be placed, relative to the - * local surface coordinates of the parent surface. See - * xdg_surface.get_popup. An xdg_popup must intersect with or be at least - * partially adjacent to its parent surface. - * * The client must call wl_surface.commit on the corresponding wl_surface * for the xdg_popup state to take effect. */ extern const struct wl_interface xdg_popup_interface; +#endif #ifndef XDG_WM_BASE_ERROR_ENUM #define XDG_WM_BASE_ERROR_ENUM @@ -587,6 +608,9 @@ enum xdg_positioner_constraint_adjustment { #define XDG_POSITIONER_SET_GRAVITY 4 #define XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT 5 #define XDG_POSITIONER_SET_OFFSET 6 +#define XDG_POSITIONER_SET_REACTIVE 7 +#define XDG_POSITIONER_SET_PARENT_SIZE 8 +#define XDG_POSITIONER_SET_PARENT_CONFIGURE 9 /** @@ -617,6 +641,18 @@ enum xdg_positioner_constraint_adjustment { * @ingroup iface_xdg_positioner */ #define XDG_POSITIONER_SET_OFFSET_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_REACTIVE_SINCE_VERSION 3 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_PARENT_SIZE_SINCE_VERSION 3 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_PARENT_CONFIGURE_SINCE_VERSION 3 /** @ingroup iface_xdg_positioner */ static inline void @@ -769,6 +805,56 @@ xdg_positioner_set_offset(struct xdg_positioner *xdg_positioner, int32_t x, int3 XDG_POSITIONER_SET_OFFSET, x, y); } +/** + * @ingroup iface_xdg_positioner + * + * When set reactive, the surface is reconstrained if the conditions used + * for constraining changed, e.g. the parent window moved. + * + * If the conditions changed and the popup was reconstrained, an + * xdg_popup.configure event is sent with updated geometry, followed by an + * xdg_surface.configure event. + */ +static inline void +xdg_positioner_set_reactive(struct xdg_positioner *xdg_positioner) +{ + wl_proxy_marshal((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_REACTIVE); +} + +/** + * @ingroup iface_xdg_positioner + * + * Set the parent window geometry the compositor should use when + * positioning the popup. The compositor may use this information to + * determine the future state the popup should be constrained using. If + * this doesn't match the dimension of the parent the popup is eventually + * positioned against, the behavior is undefined. + * + * The arguments are given in the surface-local coordinate space. + */ +static inline void +xdg_positioner_set_parent_size(struct xdg_positioner *xdg_positioner, int32_t parent_width, int32_t parent_height) +{ + wl_proxy_marshal((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_PARENT_SIZE, parent_width, parent_height); +} + +/** + * @ingroup iface_xdg_positioner + * + * Set the serial of an xdg_surface.configure event this positioner will be + * used in response to. The compositor may use this information together + * with set_parent_size to determine what future state the popup should be + * constrained using. + */ +static inline void +xdg_positioner_set_parent_configure(struct xdg_positioner *xdg_positioner, uint32_t serial) +{ + wl_proxy_marshal((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_PARENT_CONFIGURE, serial); +} + #ifndef XDG_SURFACE_ERROR_ENUM #define XDG_SURFACE_ERROR_ENUM enum xdg_surface_error { @@ -1691,6 +1777,12 @@ struct xdg_popup_listener { * The x and y arguments represent the position the popup was * placed at given the xdg_positioner rule, relative to the upper * left corner of the window geometry of the parent surface. + * + * For version 2 or older, the configure event for an xdg_popup is + * only ever sent once for the initial configuration. Starting with + * version 3, it may be sent again if the popup is setup with an + * xdg_positioner with set_reactive requested, or in response to + * xdg_popup.reposition requests. * @param x x position relative to parent surface window geometry * @param y y position relative to parent surface window geometry * @param width window geometry width @@ -1711,6 +1803,32 @@ struct xdg_popup_listener { */ void (*popup_done)(void *data, struct xdg_popup *xdg_popup); + /** + * signal the completion of a repositioned request + * + * The repositioned event is sent as part of a popup + * configuration sequence, together with xdg_popup.configure and + * lastly xdg_surface.configure to notify the completion of a + * reposition request. + * + * The repositioned event is to notify about the completion of a + * xdg_popup.reposition request. The token argument is the token + * passed in the xdg_popup.reposition request. + * + * Immediately after this event is emitted, xdg_popup.configure and + * xdg_surface.configure will be sent with the updated size and + * position, as well as a new configure serial. + * + * The client should optionally update the content of the popup, + * but must acknowledge the new popup configuration for the new + * position to take effect. See xdg_surface.ack_configure for + * details. + * @param token reposition request token + * @since 3 + */ + void (*repositioned)(void *data, + struct xdg_popup *xdg_popup, + uint32_t token); }; /** @@ -1726,6 +1844,7 @@ xdg_popup_add_listener(struct xdg_popup *xdg_popup, #define XDG_POPUP_DESTROY 0 #define XDG_POPUP_GRAB 1 +#define XDG_POPUP_REPOSITION 2 /** * @ingroup iface_xdg_popup @@ -1735,6 +1854,10 @@ xdg_popup_add_listener(struct xdg_popup *xdg_popup, * @ingroup iface_xdg_popup */ #define XDG_POPUP_POPUP_DONE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_REPOSITIONED_SINCE_VERSION 3 /** * @ingroup iface_xdg_popup @@ -1744,6 +1867,10 @@ xdg_popup_add_listener(struct xdg_popup *xdg_popup, * @ingroup iface_xdg_popup */ #define XDG_POPUP_GRAB_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_REPOSITION_SINCE_VERSION 3 /** @ingroup iface_xdg_popup */ static inline void @@ -1835,6 +1962,40 @@ xdg_popup_grab(struct xdg_popup *xdg_popup, struct wl_seat *seat, uint32_t seria XDG_POPUP_GRAB, seat, serial); } +/** + * @ingroup iface_xdg_popup + * + * Reposition an already-mapped popup. The popup will be placed given the + * details in the passed xdg_positioner object, and a + * xdg_popup.repositioned followed by xdg_popup.configure and + * xdg_surface.configure will be emitted in response. Any parameters set + * by the previous positioner will be discarded. + * + * The passed token will be sent in the corresponding + * xdg_popup.repositioned event. The new popup position will not take + * effect until the corresponding configure event is acknowledged by the + * client. See xdg_popup.repositioned for details. The token itself is + * opaque, and has no other special meaning. + * + * If multiple reposition requests are sent, the compositor may skip all + * but the last one. + * + * If the popup is repositioned in response to a configure event for its + * parent, the client should send an xdg_positioner.set_parent_configure + * and possibly an xdg_positioner.set_parent_size request to allow the + * compositor to properly constrain the popup. + * + * If the popup is repositioned together with a parent that is being + * resized, but not in response to a configure event, the client should + * send an xdg_positioner.set_parent_size request. + */ +static inline void +xdg_popup_reposition(struct xdg_popup *xdg_popup, struct xdg_positioner *positioner, uint32_t token) +{ + wl_proxy_marshal((struct wl_proxy *) xdg_popup, + XDG_POPUP_REPOSITION, positioner, token); +} + #ifdef __cplusplus } #endif diff --git a/vendor/gioui.org/app/window.go b/vendor/gioui.org/app/window.go index 2c9860c..ada92fb 100644 --- a/vendor/gioui.org/app/window.go +++ b/vendor/gioui.org/app/window.go @@ -6,10 +6,14 @@ import ( "errors" "fmt" "image" + "image/color" + "runtime" "time" - "gioui.org/app/internal/window" + "gioui.org/f32" + "gioui.org/gpu" "gioui.org/io/event" + "gioui.org/io/pointer" "gioui.org/io/profile" "gioui.org/io/router" "gioui.org/io/system" @@ -19,24 +23,32 @@ import ( _ "gioui.org/app/internal/log" ) -// WindowOption configures a Window. -type Option func(opts *window.Options) +// Option configures a window. +type Option func(unit.Metric, *Config) // Window represents an operating system window. type Window struct { - driver window.Driver - loop *renderLoop + ctx context + gpu gpu.GPU // driverFuncs is a channel of functions to run when // the Window has a valid driver. - driverFuncs chan func() - - out chan event.Event - in chan event.Event - ack chan struct{} - invalidates chan struct{} - frames chan *op.Ops - frameAck chan struct{} + driverFuncs chan func(d driver) + // wakeups wakes up the native event loop to send a + // WakeupEvent that flushes driverFuncs. + wakeups chan struct{} + // wakeupFuncs is sent wakeup functions when the driver changes. + wakeupFuncs chan func() + // redraws is notified when a redraw is requested by the client. + redraws chan struct{} + // immediateRedraws is like redraw but doesn't need a wakeup. + immediateRedraws chan struct{} + // scheduledRedraws is sent the most recent delayed redraw time. + scheduledRedraws chan time.Time + + out chan event.Event + frames chan *op.Ops + frameAck chan struct{} // dead is closed when the window is destroyed. dead chan struct{} @@ -46,13 +58,33 @@ type Window struct { nextFrame time.Time delayedDraw *time.Timer - queue queue + queue queue + cursor pointer.CursorName callbacks callbacks + + nocontext bool + + // semantic data, lazily evaluated if requested by a backend to speed up + // the cases where semantic data is not needed. + semantic struct { + // uptodate tracks whether the fields below are up to date. + uptodate bool + root router.SemanticID + prevTree []router.SemanticNode + tree []router.SemanticNode + ids map[router.SemanticID]router.SemanticNode + } +} + +type semanticResult struct { + found bool + node router.SemanticNode } type callbacks struct { w *Window + d driver } // queue is an event.Queue implementation that distributes system events @@ -61,12 +93,6 @@ type queue struct { q router.Router } -// driverEvent is sent when a new native driver -// is available for the Window. -type driverEvent struct { - driver window.Driver -} - // Pre-allocate the ack event to avoid garbage. var ackEvent event.Event @@ -74,35 +100,37 @@ var ackEvent event.Event // options. The options are hints; the platform is free to // ignore or adjust them. // -// If the current program is running on iOS and Android, +// If the current program is running on iOS or Android, // NewWindow returns the window previously created by the // platform. // // Calling NewWindow more than once is not supported on // iOS, Android, WebAssembly. func NewWindow(options ...Option) *Window { - opts := &window.Options{ - Width: unit.Dp(800), - Height: unit.Dp(600), - Title: "Gio", - } - - for _, o := range options { - o(opts) + defaultOptions := []Option{ + Size(unit.Dp(800), unit.Dp(600)), + Title("Gio"), } + options = append(defaultOptions, options...) + var cnf Config + cnf.apply(unit.Metric{}, options) w := &Window{ - in: make(chan event.Event), - out: make(chan event.Event), - ack: make(chan struct{}), - invalidates: make(chan struct{}, 1), - frames: make(chan *op.Ops), - frameAck: make(chan struct{}), - driverFuncs: make(chan func()), - dead: make(chan struct{}), + out: make(chan event.Event), + immediateRedraws: make(chan struct{}, 0), + redraws: make(chan struct{}, 1), + scheduledRedraws: make(chan time.Time, 1), + frames: make(chan *op.Ops), + frameAck: make(chan struct{}), + driverFuncs: make(chan func(d driver), 1), + wakeups: make(chan struct{}, 1), + wakeupFuncs: make(chan func()), + dead: make(chan struct{}), + nocontext: cnf.CustomRenderer, } + w.semantic.ids = make(map[router.SemanticID]router.SemanticNode) w.callbacks.w = w - go w.run(opts) + go w.run(options) return w } @@ -111,141 +139,260 @@ func (w *Window) Events() <-chan event.Event { return w.out } -// update updates the Window. Paint operations updates the -// window contents, input operations declare input handlers, -// and so on. The supplied operations list completely replaces -// the window state from previous calls. +// update updates the window contents, input operations declare input handlers, +// and so on. The supplied operations list completely replaces the window state +// from previous calls. func (w *Window) update(frame *op.Ops) { w.frames <- frame <-w.frameAck } -func (w *Window) validateAndProcess(frameStart time.Time, size image.Point, sync bool, frame *op.Ops) error { +func (w *Window) validateAndProcess(d driver, frameStart time.Time, size image.Point, sync bool, frame *op.Ops) error { for { - if w.loop != nil { - if err := w.loop.Flush(); err != nil { + if w.gpu == nil && !w.nocontext { + var err error + if w.ctx == nil { + w.ctx, err = d.NewContext() + if err != nil { + return err + } + sync = true + } + } + if sync && w.ctx != nil { + if err := w.ctx.Refresh(); err != nil { + if errors.Is(err, errOutOfDate) { + // Surface couldn't be created for transient reasons. Skip + // this frame and wait for the next. + return nil + } w.destroyGPU() - if err == window.ErrDeviceLost { + if errors.Is(err, gpu.ErrDeviceLost) { continue } return err } } - if w.loop == nil { - var ctx window.Context - ctx, err := w.driver.NewContext() - if err != nil { + if w.gpu == nil && !w.nocontext { + if err := w.ctx.Lock(); err != nil { + w.destroyGPU() return err } - w.loop, err = newLoop(ctx) + gpu, err := gpu.New(w.ctx.API()) + w.ctx.Unlock() if err != nil { - ctx.Release() + w.destroyGPU() return err } + w.gpu = gpu } - w.processFrame(frameStart, size, frame) - if sync { - if err := w.loop.Flush(); err != nil { + if w.gpu != nil { + if err := w.render(frame, size); err != nil { + if errors.Is(err, errOutOfDate) { + // GPU surface needs refreshing. + sync = true + continue + } w.destroyGPU() - if err == window.ErrDeviceLost { + if errors.Is(err, gpu.ErrDeviceLost) { continue } return err } } + w.processFrame(d, frameStart, frame) return nil } } -func (w *Window) processFrame(frameStart time.Time, size image.Point, frame *op.Ops) { - sync := w.loop.Draw(size, frame) +func (w *Window) render(frame *op.Ops, viewport image.Point) error { + if err := w.ctx.Lock(); err != nil { + return err + } + defer w.ctx.Unlock() + if runtime.GOOS == "js" { + // Use transparent black when Gio is embedded, to allow mixing of Gio and + // foreign content below. + w.gpu.Clear(color.NRGBA{A: 0x00, R: 0x00, G: 0x00, B: 0x00}) + } else { + w.gpu.Clear(color.NRGBA{A: 0xff, R: 0xff, G: 0xff, B: 0xff}) + } + target, err := w.ctx.RenderTarget() + if err != nil { + return err + } + if err := w.gpu.Frame(frame, target, viewport); err != nil { + return err + } + return w.ctx.Present() +} + +func (w *Window) processFrame(d driver, frameStart time.Time, frame *op.Ops) { w.queue.q.Frame(frame) + for k := range w.semantic.ids { + delete(w.semantic.ids, k) + } + w.semantic.uptodate = false switch w.queue.q.TextInputState() { case router.TextInputOpen: - w.driver.ShowTextInput(true) + d.ShowTextInput(true) case router.TextInputClose: - w.driver.ShowTextInput(false) + d.ShowTextInput(false) + } + if hint, ok := w.queue.q.TextInputHint(); ok { + d.SetInputHint(hint) + } + if txt, ok := w.queue.q.WriteClipboard(); ok { + w.WriteClipboard(txt) + } + if w.queue.q.ReadClipboard() { + w.ReadClipboard() } - if w.queue.q.Profiling() { + if w.queue.q.Profiling() && w.gpu != nil { frameDur := time.Since(frameStart) frameDur = frameDur.Truncate(100 * time.Microsecond) q := 100 * time.Microsecond - timings := fmt.Sprintf("tot:%7s %s", frameDur.Round(q), w.loop.Summary()) - w.queue.q.Add(profile.Event{Timings: timings}) + timings := fmt.Sprintf("tot:%7s %s", frameDur.Round(q), w.gpu.Profile()) + w.queue.q.Queue(profile.Event{Timings: timings}) } if t, ok := w.queue.q.WakeupTime(); ok { w.setNextFrame(t) } - w.updateAnimation() - // Wait for the GPU goroutine to finish processing frame. - <-sync + w.updateAnimation(d) } -// Invalidate the window such that a FrameEvent will be generated -// immediately. If the window is inactive, the event is sent when the -// window becomes active. +// Invalidate the window such that a FrameEvent will be generated immediately. +// If the window is inactive, the event is sent when the window becomes active. +// +// Note that Invalidate is intended for externally triggered updates, such as a +// response from a network request. InvalidateOp is more efficient for animation +// and similar internal updates. +// // Invalidate is safe for concurrent use. func (w *Window) Invalidate() { select { - case w.invalidates <- struct{}{}: + case w.immediateRedraws <- struct{}{}: + return default: } + select { + case w.redraws <- struct{}{}: + w.wakeup() + default: + } +} + +// Option applies the options to the window. +func (w *Window) Option(opts ...Option) { + w.driverDefer(func(d driver) { + d.Configure(opts) + }) } // ReadClipboard initiates a read of the clipboard in the form -// of a system.ClipboardEvent. Multiple reads may be coalescedd +// of a clipboard.Event. Multiple reads may be coalesced // to a single event. func (w *Window) ReadClipboard() { - w.driverDo(func() { - w.driver.ReadClipboard() + w.driverDefer(func(d driver) { + d.ReadClipboard() }) } // WriteClipboard writes a string to the clipboard. func (w *Window) WriteClipboard(s string) { - w.driverDo(func() { - w.driver.WriteClipboard(s) + w.driverDefer(func(d driver) { + d.WriteClipboard(s) + }) +} + +// SetCursorName changes the current window cursor to name. +func (w *Window) SetCursorName(name pointer.CursorName) { + w.driverDefer(func(d driver) { + d.SetCursor(name) }) } // Close the window. The window's event loop should exit when it receives // system.DestroyEvent. // -// Currently, only macOS, Windows and X11 drivers implement this functionality, +// Currently, only macOS, Windows, X11 and Wayland drivers implement this functionality, // all others are stubbed. func (w *Window) Close() { - w.driverDo(func() { - w.driver.Close() + w.driverDefer(func(d driver) { + d.Close() }) } -// driverDo calls f as soon as the window has a valid driver attached, -// or does nothing if the window is destroyed while waiting. -func (w *Window) driverDo(f func()) { - go func() { - select { - case w.driverFuncs <- f: - case <-w.dead: - } - }() +// Maximize the window. +// Note: only implemented on Windows, macOS and X11. +func (w *Window) Maximize() { + w.driverDefer(func(d driver) { + d.Maximize() + }) } -func (w *Window) updateAnimation() { - animate := false - if w.delayedDraw != nil { - w.delayedDraw.Stop() - w.delayedDraw = nil +// Center the window. +// Note: only implemented on Windows, macOS and X11. +func (w *Window) Center() { + w.driverDefer(func(d driver) { + d.Center() + }) +} + +// Run f in the same thread as the native window event loop, and wait for f to +// return or the window to close. Run is guaranteed not to deadlock if it is +// invoked during the handling of a ViewEvent, system.FrameEvent, +// system.StageEvent; call Run in a separate goroutine to avoid deadlock in all +// other cases. +// +// Note that most programs should not call Run; configuring a Window with +// CustomRenderer is a notable exception. +func (w *Window) Run(f func()) { + done := make(chan struct{}) + w.driverDefer(func(d driver) { + defer close(done) + f() + }) + select { + case <-done: + case <-w.dead: } +} + +// driverDefer is like Run but can be run from any context. It doesn't wait +// for f to return. +func (w *Window) driverDefer(f func(d driver)) { + select { + case w.driverFuncs <- f: + w.wakeup() + case <-w.dead: + } +} + +func (w *Window) updateAnimation(d driver) { + animate := false if w.stage >= system.StageRunning && w.hasNextFrame { if dt := time.Until(w.nextFrame); dt <= 0 { animate = true } else { - w.delayedDraw = time.NewTimer(dt) + // Schedule redraw. + select { + case <-w.scheduledRedraws: + default: + } + w.scheduledRedraws <- w.nextFrame } } if animate != w.animating { w.animating = animate - w.driver.SetAnimating(animate) + d.SetAnimating(animate) + } +} + +func (w *Window) wakeup() { + select { + case w.wakeups <- struct{}{}: + default: } } @@ -256,164 +403,289 @@ func (w *Window) setNextFrame(at time.Time) { } } -func (c *callbacks) SetDriver(d window.Driver) { - c.Event(driverEvent{d}) +func (c *callbacks) SetDriver(d driver) { + c.d = d + var wakeup func() + if d != nil { + wakeup = d.Wakeup + } + c.w.wakeupFuncs <- wakeup } func (c *callbacks) Event(e event.Event) { - select { - case c.w.in <- e: - <-c.w.ack - case <-c.w.dead: + if c.d == nil { + panic("event while no driver active") } + c.w.processEvent(c.d, e) + c.w.updateState(c.d) +} + +// SemanticRoot returns the ID of the semantic root. +func (c *callbacks) SemanticRoot() router.SemanticID { + c.w.updateSemantics() + return c.w.semantic.root } -func (w *Window) waitAck() { - // Send a dummy event; when it gets through we - // know the application has processed the previous event. - w.out <- ackEvent +// LookupSemantic looks up a semantic node from an ID. The zero ID denotes the root. +func (c *callbacks) LookupSemantic(semID router.SemanticID) (router.SemanticNode, bool) { + c.w.updateSemantics() + n, found := c.w.semantic.ids[semID] + return n, found } -// Prematurely destroy the window and wait for the native window -// destroy event. -func (w *Window) destroy(err error) { - w.destroyGPU() - // Ack the current event. - w.ack <- struct{}{} - w.out <- system.DestroyEvent{Err: err} - close(w.dead) - for e := range w.in { - w.ack <- struct{}{} - if _, ok := e.(system.DestroyEvent); ok { +func (c *callbacks) AppendSemanticDiffs(diffs []router.SemanticID) []router.SemanticID { + c.w.updateSemantics() + if tree := c.w.semantic.prevTree; len(tree) > 0 { + c.w.collectSemanticDiffs(&diffs, c.w.semantic.prevTree[0]) + } + return diffs +} + +func (c *callbacks) SemanticAt(pos f32.Point) (router.SemanticID, bool) { + c.w.updateSemantics() + return c.w.queue.q.SemanticAt(pos) +} + +func (w *Window) waitAck(d driver) { + for { + select { + case f := <-w.driverFuncs: + f(d) + case w.out <- ackEvent: + // A dummy event went through, so we know the application has processed the previous event. return + case <-w.immediateRedraws: + // Invalidate was called during frame processing. + w.setNextFrame(time.Time{}) } } } func (w *Window) destroyGPU() { - if w.loop != nil { - w.loop.Release() - w.loop = nil + if w.gpu != nil { + w.ctx.Lock() + w.gpu.Release() + w.ctx.Unlock() + w.gpu = nil + } + if w.ctx != nil { + w.ctx.Release() + w.ctx = nil } } // waitFrame waits for the client to either call FrameEvent.Frame // or to continue event handling. It returns whether the client // called Frame or not. -func (w *Window) waitFrame() (*op.Ops, bool) { +func (w *Window) waitFrame(d driver) (*op.Ops, bool) { + for { + select { + case f := <-w.driverFuncs: + f(d) + case frame := <-w.frames: + // The client called FrameEvent.Frame. + return frame, true + case w.out <- ackEvent: + // The client ignored FrameEvent and continued processing + // events. + return nil, false + case <-w.immediateRedraws: + // Invalidate was called during frame processing. + w.setNextFrame(time.Time{}) + } + } +} + +// updateSemantics refreshes the semantics tree, the id to node map and the ids of +// updated nodes. +func (w *Window) updateSemantics() { + if w.semantic.uptodate { + return + } + w.semantic.uptodate = true + w.semantic.prevTree, w.semantic.tree = w.semantic.tree, w.semantic.prevTree + w.semantic.tree = w.queue.q.AppendSemantics(w.semantic.tree[:0]) + w.semantic.root = w.semantic.tree[0].ID + for _, n := range w.semantic.tree { + w.semantic.ids[n.ID] = n + } +} + +// collectSemanticDiffs traverses the previous semantic tree, noting changed nodes. +func (w *Window) collectSemanticDiffs(diffs *[]router.SemanticID, n router.SemanticNode) { + newNode, exists := w.semantic.ids[n.ID] + // Ignore deleted nodes, as their disappearance will be reported through an + // ancestor node. + if !exists { + return + } + diff := newNode.Desc != n.Desc || len(n.Children) != len(newNode.Children) + for i, ch := range n.Children { + if !diff { + newCh := newNode.Children[i] + diff = ch.ID != newCh.ID + } + w.collectSemanticDiffs(diffs, ch) + } + if diff { + *diffs = append(*diffs, n.ID) + } +} + +func (w *Window) updateState(d driver) { + for { + select { + case f := <-w.driverFuncs: + f(d) + case <-w.redraws: + w.setNextFrame(time.Time{}) + w.updateAnimation(d) + default: + return + } + } +} + +func (w *Window) processEvent(d driver, e event.Event) { select { - case frame := <-w.frames: - // The client called FrameEvent.Frame. - return frame, true - case w.out <- ackEvent: - // The client ignored FrameEvent and continued processing - // events. - return nil, false + case <-w.dead: + return + default: + } + switch e2 := e.(type) { + case system.StageEvent: + if e2.Stage < system.StageRunning { + if w.gpu != nil { + w.ctx.Lock() + w.gpu.Release() + w.gpu = nil + w.ctx.Unlock() + } + } + w.stage = e2.Stage + w.updateAnimation(d) + w.out <- e + w.waitAck(d) + case frameEvent: + if e2.Size == (image.Point{}) { + panic(errors.New("internal error: zero-sized Draw")) + } + if w.stage < system.StageRunning { + // No drawing if not visible. + break + } + frameStart := time.Now() + w.hasNextFrame = false + e2.Frame = w.update + e2.Queue = &w.queue + w.out <- e2.FrameEvent + frame, gotFrame := w.waitFrame(d) + err := w.validateAndProcess(d, frameStart, e2.Size, e2.Sync, frame) + if gotFrame { + // We're done with frame, let the client continue. + w.frameAck <- struct{}{} + } + if err != nil { + w.destroyGPU() + w.out <- system.DestroyEvent{Err: err} + close(w.dead) + close(w.out) + break + } + w.updateCursor() + case *system.CommandEvent: + w.out <- e + w.waitAck(d) + case system.DestroyEvent: + w.destroyGPU() + w.out <- e2 + close(w.dead) + close(w.out) + case ViewEvent: + w.out <- e2 + w.waitAck(d) + case wakeupEvent: + case event.Event: + if w.queue.q.Queue(e2) { + w.setNextFrame(time.Time{}) + w.updateAnimation(d) + } + w.updateCursor() + w.out <- e } } -func (w *Window) run(opts *window.Options) { - defer close(w.in) - defer close(w.out) - if err := window.NewWindow(&w.callbacks, opts); err != nil { +func (w *Window) run(options []Option) { + if err := newWindow(&w.callbacks, options); err != nil { w.out <- system.DestroyEvent{Err: err} + close(w.dead) + close(w.out) return } + var wakeup func() + var timer *time.Timer for { - var driverFuncs chan func() - if w.driver != nil { - driverFuncs = w.driverFuncs - } - var timer <-chan time.Time - if w.delayedDraw != nil { - timer = w.delayedDraw.C + var ( + wakeups <-chan struct{} + timeC <-chan time.Time + ) + if wakeup != nil { + wakeups = w.wakeups + if timer != nil { + timeC = timer.C + } } select { - case <-timer: - w.setNextFrame(time.Time{}) - w.updateAnimation() - case <-w.invalidates: - w.setNextFrame(time.Time{}) - w.updateAnimation() - case f := <-driverFuncs: - f() - case e := <-w.in: - switch e2 := e.(type) { - case system.StageEvent: - if w.loop != nil { - if e2.Stage < system.StageRunning { - w.destroyGPU() - } else { - w.loop.Refresh() - } - } - w.stage = e2.Stage - w.updateAnimation() - w.out <- e - w.waitAck() - case window.FrameEvent: - if e2.Size == (image.Point{}) { - panic(errors.New("internal error: zero-sized Draw")) - } - if w.stage < system.StageRunning { - // No drawing if not visible. - break - } - frameStart := time.Now() - w.hasNextFrame = false - e2.Frame = w.update - e2.Queue = &w.queue - w.out <- e2.FrameEvent - if w.loop != nil { - if e2.Sync { - w.loop.Refresh() - } - } - frame, gotFrame := w.waitFrame() - err := w.validateAndProcess(frameStart, e2.Size, e2.Sync, frame) - if gotFrame { - // We're done with frame, let the client continue. - w.frameAck <- struct{}{} - } - if err != nil { - w.destroyGPU() - w.destroy(err) - return - } - case *system.CommandEvent: - w.out <- e - w.waitAck() - case driverEvent: - w.driver = e2.driver - case system.DestroyEvent: - w.destroyGPU() - w.out <- e2 - w.ack <- struct{}{} - return - case event.Event: - if w.queue.q.Add(e2) { - w.setNextFrame(time.Time{}) - w.updateAnimation() - } - w.out <- e + case t := <-w.scheduledRedraws: + if timer != nil { + timer.Stop() } - w.ack <- struct{}{} + timer = time.NewTimer(time.Until(t)) + case <-w.dead: + return + case <-timeC: + select { + case w.redraws <- struct{}{}: + wakeup() + default: + } + case <-wakeups: + wakeup() + case wakeup = <-w.wakeupFuncs: } } } +func (w *Window) updateCursor() { + if c := w.queue.q.Cursor(); c != w.cursor { + w.cursor = c + w.SetCursorName(c) + } +} + +// Raise requests that the platform bring this window to the top of all open windows. +// Some platforms do not allow this except under certain circumstances, such as when +// a window from the same application already has focus. If the platform does not +// support it, this method will do nothing. +func (w *Window) Raise() { + w.driverDefer(func(d driver) { + d.Raise() + }) +} + func (q *queue) Events(k event.Tag) []event.Event { return q.q.Events(k) } // Title sets the title of the window. func Title(t string) Option { - return func(opts *window.Options) { - opts.Title = t + return func(_ unit.Metric, cnf *Config) { + cnf.Title = t } } -// Size sets the size of the window. +// Size sets the size of the window. The option is ignored +// in Fullscreen mode. func Size(w, h unit.Value) Option { if w.V <= 0 { panic("width must be larger than or equal to 0") @@ -421,9 +693,11 @@ func Size(w, h unit.Value) Option { if h.V <= 0 { panic("height must be larger than or equal to 0") } - return func(opts *window.Options) { - opts.Width = w - opts.Height = h + return func(m unit.Metric, cnf *Config) { + cnf.Size = image.Point{ + X: m.Px(w), + Y: m.Px(h), + } } } @@ -435,9 +709,11 @@ func MaxSize(w, h unit.Value) Option { if h.V <= 0 { panic("height must be larger than or equal to 0") } - return func(opts *window.Options) { - opts.MaxWidth = w - opts.MaxHeight = h + return func(m unit.Metric, cnf *Config) { + cnf.MaxSize = image.Point{ + X: m.Px(w), + Y: m.Px(h), + } } } @@ -449,10 +725,32 @@ func MinSize(w, h unit.Value) Option { if h.V <= 0 { panic("height must be larger than or equal to 0") } - return func(opts *window.Options) { - opts.MinWidth = w - opts.MinHeight = h + return func(m unit.Metric, cnf *Config) { + cnf.MinSize = image.Point{ + X: m.Px(w), + Y: m.Px(h), + } } } -func (driverEvent) ImplementsEvent() {} +// StatusColor sets the color of the Android status bar. +func StatusColor(color color.NRGBA) Option { + return func(_ unit.Metric, cnf *Config) { + cnf.StatusColor = color + } +} + +// NavigationColor sets the color of the navigation bar on Android, or the address bar in browsers. +func NavigationColor(color color.NRGBA) Option { + return func(_ unit.Metric, cnf *Config) { + cnf.NavigationColor = color + } +} + +// CustomRenderer controls whether the window contents is +// rendered by the client. If true, no GPU context is created. +func CustomRenderer(custom bool) Option { + return func(_ unit.Metric, cnf *Config) { + cnf.CustomRenderer = custom + } +} |