mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-07 10:25:37 +00:00
app/internal/wm: rename confusing app/internal/window
Package wm (for "window manager") is a better fit for the package, and distinguishes it from the low-level package windows for the Windows API. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
@@ -0,0 +1,710 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
package wm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode"
|
||||
"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 winDeltas struct {
|
||||
width int32
|
||||
height int32
|
||||
}
|
||||
|
||||
type window struct {
|
||||
hwnd syscall.Handle
|
||||
hdc syscall.Handle
|
||||
w Callbacks
|
||||
width int
|
||||
height int
|
||||
stage system.Stage
|
||||
pointerBtns pointer.Buttons
|
||||
|
||||
// cursorIn tracks whether the cursor was inside the window according
|
||||
// to the most recent WM_SETCURSOR.
|
||||
cursorIn bool
|
||||
cursor syscall.Handle
|
||||
|
||||
mu sync.Mutex
|
||||
animating bool
|
||||
|
||||
minmax winConstraints
|
||||
deltas winDeltas
|
||||
opts *Options
|
||||
}
|
||||
|
||||
const (
|
||||
_WM_REDRAW = windows.WM_USER + iota
|
||||
_WM_CURSOR
|
||||
)
|
||||
|
||||
type gpuAPI struct {
|
||||
priority int
|
||||
initializer func(w *window) (Context, error)
|
||||
}
|
||||
|
||||
// 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 syscall.Handle
|
||||
}
|
||||
|
||||
func Main() {
|
||||
select {}
|
||||
}
|
||||
|
||||
func NewWindow(window Callbacks, opts *Options) error {
|
||||
cerr := make(chan error)
|
||||
go func() {
|
||||
// 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)
|
||||
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{})
|
||||
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)
|
||||
}
|
||||
}()
|
||||
return <-cerr
|
||||
}
|
||||
|
||||
// initResources initializes the resources global.
|
||||
func initResources() error {
|
||||
windows.SetProcessDPIAware()
|
||||
hInst, err := windows.GetModuleHandle()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resources.handle = hInst
|
||||
c, err := windows.LoadCursor(windows.IDC_ARROW)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
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,
|
||||
HIcon: icon,
|
||||
LpszClassName: syscall.StringToUTF16Ptr("GioWindow"),
|
||||
}
|
||||
cls, err := windows.RegisterClassEx(&wcls)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resources.class = cls
|
||||
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) {
|
||||
var resErr error
|
||||
resources.once.Do(func() {
|
||||
resErr = initResources()
|
||||
})
|
||||
if resErr != nil {
|
||||
return nil, resErr
|
||||
}
|
||||
dpi := windows.GetSystemDPI()
|
||||
cfg := configForDPI(dpi)
|
||||
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,
|
||||
0,
|
||||
0,
|
||||
resources.handle,
|
||||
0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
w := &window{
|
||||
hwnd: hwnd,
|
||||
minmax: getWindowConstraints(cfg, opts, deltas),
|
||||
deltas: deltas,
|
||||
opts: opts,
|
||||
}
|
||||
w.hdc, err = windows.GetDC(hwnd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return w, nil
|
||||
}
|
||||
|
||||
func windowProc(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) uintptr {
|
||||
win, exists := winMap.Load(hwnd)
|
||||
if !exists {
|
||||
return windows.DefWindowProc(hwnd, msg, wParam, lParam)
|
||||
}
|
||||
|
||||
w := win.(*window)
|
||||
|
||||
switch msg {
|
||||
case windows.WM_UNICHAR:
|
||||
if wParam == windows.UNICODE_NOCHAR {
|
||||
// Tell the system that we accept WM_UNICHAR messages.
|
||||
return windows.TRUE
|
||||
}
|
||||
fallthrough
|
||||
case windows.WM_CHAR:
|
||||
if r := rune(wParam); unicode.IsPrint(r) {
|
||||
w.w.Event(key.EditEvent{Text: string(r)})
|
||||
}
|
||||
// The message is processed.
|
||||
return windows.TRUE
|
||||
case windows.WM_DPICHANGED:
|
||||
// Let Windows know we're prepared for runtime DPI changes.
|
||||
return windows.TRUE
|
||||
case windows.WM_ERASEBKGND:
|
||||
// Avoid flickering between GPU content and background color.
|
||||
return windows.TRUE
|
||||
case windows.WM_KEYDOWN, windows.WM_KEYUP, windows.WM_SYSKEYDOWN, windows.WM_SYSKEYUP:
|
||||
if n, ok := convertKeyCode(wParam); ok {
|
||||
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)
|
||||
}
|
||||
case windows.WM_LBUTTONDOWN:
|
||||
w.pointerButton(pointer.ButtonPrimary, true, lParam, getModifiers())
|
||||
case windows.WM_LBUTTONUP:
|
||||
w.pointerButton(pointer.ButtonPrimary, false, lParam, getModifiers())
|
||||
case windows.WM_RBUTTONDOWN:
|
||||
w.pointerButton(pointer.ButtonSecondary, true, lParam, getModifiers())
|
||||
case windows.WM_RBUTTONUP:
|
||||
w.pointerButton(pointer.ButtonSecondary, false, lParam, getModifiers())
|
||||
case windows.WM_MBUTTONDOWN:
|
||||
w.pointerButton(pointer.ButtonTertiary, true, lParam, getModifiers())
|
||||
case windows.WM_MBUTTONUP:
|
||||
w.pointerButton(pointer.ButtonTertiary, false, lParam, getModifiers())
|
||||
case windows.WM_CANCELMODE:
|
||||
w.w.Event(pointer.Event{
|
||||
Type: pointer.Cancel,
|
||||
})
|
||||
case windows.WM_SETFOCUS:
|
||||
w.w.Event(key.FocusEvent{Focus: true})
|
||||
case windows.WM_KILLFOCUS:
|
||||
w.w.Event(key.FocusEvent{Focus: false})
|
||||
case windows.WM_MOUSEMOVE:
|
||||
x, y := coordsFromlParam(lParam)
|
||||
p := f32.Point{X: float32(x), Y: float32(y)}
|
||||
w.w.Event(pointer.Event{
|
||||
Type: pointer.Move,
|
||||
Source: pointer.Mouse,
|
||||
Position: p,
|
||||
Buttons: w.pointerBtns,
|
||||
Time: windows.GetMessageTime(),
|
||||
})
|
||||
case windows.WM_MOUSEWHEEL:
|
||||
w.scrollEvent(wParam, lParam, false)
|
||||
case windows.WM_MOUSEHWHEEL:
|
||||
w.scrollEvent(wParam, lParam, true)
|
||||
case windows.WM_DESTROY:
|
||||
windows.PostQuitMessage(0)
|
||||
case windows.WM_PAINT:
|
||||
w.draw(true)
|
||||
case windows.WM_SIZE:
|
||||
switch wParam {
|
||||
case windows.SIZE_MINIMIZED:
|
||||
w.setStage(system.StagePaused)
|
||||
case windows.SIZE_MAXIMIZED, windows.SIZE_RESTORED:
|
||||
w.setStage(system.StageRunning)
|
||||
}
|
||||
case windows.WM_GETMINMAXINFO:
|
||||
mm := (*windows.MinMaxInfo)(unsafe.Pointer(uintptr(lParam)))
|
||||
if w.minmax.minWidth > 0 || w.minmax.minHeight > 0 {
|
||||
mm.PtMinTrackSize = windows.Point{
|
||||
X: w.minmax.minWidth + w.deltas.width,
|
||||
Y: w.minmax.minHeight + w.deltas.height,
|
||||
}
|
||||
}
|
||||
if w.minmax.maxWidth > 0 || w.minmax.maxHeight > 0 {
|
||||
mm.PtMaxTrackSize = windows.Point{
|
||||
X: w.minmax.maxWidth + w.deltas.width,
|
||||
Y: w.minmax.maxHeight + w.deltas.height,
|
||||
}
|
||||
}
|
||||
case windows.WM_SETCURSOR:
|
||||
w.cursorIn = (lParam & 0xffff) == windows.HTCLIENT
|
||||
fallthrough
|
||||
case _WM_CURSOR:
|
||||
if w.cursorIn {
|
||||
windows.SetCursor(w.cursor)
|
||||
return windows.TRUE
|
||||
}
|
||||
}
|
||||
|
||||
return windows.DefWindowProc(hwnd, msg, wParam, lParam)
|
||||
}
|
||||
|
||||
func getModifiers() key.Modifiers {
|
||||
var kmods key.Modifiers
|
||||
if windows.GetKeyState(windows.VK_LWIN)&0x1000 != 0 || windows.GetKeyState(windows.VK_RWIN)&0x1000 != 0 {
|
||||
kmods |= key.ModSuper
|
||||
}
|
||||
if windows.GetKeyState(windows.VK_MENU)&0x1000 != 0 {
|
||||
kmods |= key.ModAlt
|
||||
}
|
||||
if windows.GetKeyState(windows.VK_CONTROL)&0x1000 != 0 {
|
||||
kmods |= key.ModCtrl
|
||||
}
|
||||
if windows.GetKeyState(windows.VK_SHIFT)&0x1000 != 0 {
|
||||
kmods |= key.ModShift
|
||||
}
|
||||
return kmods
|
||||
}
|
||||
|
||||
func (w *window) pointerButton(btn pointer.Buttons, press bool, lParam uintptr, kmods key.Modifiers) {
|
||||
var typ pointer.Type
|
||||
if press {
|
||||
typ = pointer.Press
|
||||
if w.pointerBtns == 0 {
|
||||
windows.SetCapture(w.hwnd)
|
||||
}
|
||||
w.pointerBtns |= btn
|
||||
} else {
|
||||
typ = pointer.Release
|
||||
w.pointerBtns &^= btn
|
||||
if w.pointerBtns == 0 {
|
||||
windows.ReleaseCapture()
|
||||
}
|
||||
}
|
||||
x, y := coordsFromlParam(lParam)
|
||||
p := f32.Point{X: float32(x), Y: float32(y)}
|
||||
w.w.Event(pointer.Event{
|
||||
Type: typ,
|
||||
Source: pointer.Mouse,
|
||||
Position: p,
|
||||
Buttons: w.pointerBtns,
|
||||
Time: windows.GetMessageTime(),
|
||||
Modifiers: kmods,
|
||||
})
|
||||
}
|
||||
|
||||
func coordsFromlParam(lParam uintptr) (int, int) {
|
||||
x := int(int16(lParam & 0xffff))
|
||||
y := int(int16((lParam >> 16) & 0xffff))
|
||||
return x, y
|
||||
}
|
||||
|
||||
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.
|
||||
np := windows.Point{X: int32(x), Y: int32(y)}
|
||||
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: sp,
|
||||
Time: windows.GetMessageTime(),
|
||||
})
|
||||
}
|
||||
|
||||
// Adapted from https://blogs.msdn.microsoft.com/oldnewthing/20060126-00/?p=32513/
|
||||
func (w *window) loop() error {
|
||||
msg := new(windows.Msg)
|
||||
loop:
|
||||
for {
|
||||
w.mu.Lock()
|
||||
anim := w.animating
|
||||
w.mu.Unlock()
|
||||
if anim && !windows.PeekMessage(msg, 0, 0, 0, windows.PM_NOREMOVE) {
|
||||
w.draw(false)
|
||||
continue
|
||||
}
|
||||
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)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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 {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *window) setStage(s system.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 {
|
||||
return
|
||||
}
|
||||
dpi := windows.GetWindowDPI(w.hwnd)
|
||||
cfg := configForDPI(dpi)
|
||||
w.minmax = getWindowConstraints(cfg, w.opts, w.deltas)
|
||||
w.w.Event(FrameEvent{
|
||||
FrameEvent: system.FrameEvent{
|
||||
Now: time.Now(),
|
||||
Size: image.Point{
|
||||
X: w.width,
|
||||
Y: w.height,
|
||||
},
|
||||
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(drivers, func(i, j int) bool {
|
||||
return drivers[i].priority < drivers[j].priority
|
||||
})
|
||||
var errs []string
|
||||
for _, b := range drivers {
|
||||
ctx, err := b.initializer(w)
|
||||
if err == nil {
|
||||
return ctx, nil
|
||||
}
|
||||
errs = append(errs, err.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 GPU drivers")
|
||||
}
|
||||
|
||||
func (w *window) ReadClipboard() {
|
||||
w.readClipboard()
|
||||
}
|
||||
|
||||
func (w *window) readClipboard() error {
|
||||
if err := windows.OpenClipboard(w.hwnd); err != nil {
|
||||
return err
|
||||
}
|
||||
defer windows.CloseClipboard()
|
||||
mem, err := windows.GetClipboardData(windows.CF_UNICODETEXT)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ptr, err := windows.GlobalLock(mem)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer windows.GlobalUnlock(mem)
|
||||
content := gowindows.UTF16PtrToString((*uint16)(unsafe.Pointer(ptr)))
|
||||
go func() {
|
||||
w.w.Event(clipboard.Event{Text: content})
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *window) WriteClipboard(s string) {
|
||||
w.writeClipboard(s)
|
||||
}
|
||||
|
||||
func (w *window) writeClipboard(s string) error {
|
||||
if err := windows.OpenClipboard(w.hwnd); err != nil {
|
||||
return err
|
||||
}
|
||||
defer windows.CloseClipboard()
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
ptr, err := windows.GlobalLock(mem)
|
||||
if err != nil {
|
||||
windows.GlobalFree(mem)
|
||||
return err
|
||||
}
|
||||
var u16v []uint16
|
||||
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&u16v))
|
||||
hdr.Data = ptr
|
||||
hdr.Cap = len(u16)
|
||||
hdr.Len = len(u16)
|
||||
copy(u16v, u16)
|
||||
windows.GlobalUnlock(mem)
|
||||
if err := windows.SetClipboardData(windows.CF_UNICODETEXT, mem); err != nil {
|
||||
windows.GlobalFree(mem)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *window) SetCursor(name pointer.CursorName) {
|
||||
c, err := loadCursor(name)
|
||||
if err != nil {
|
||||
c = resources.cursor
|
||||
}
|
||||
w.cursor = c
|
||||
if err := windows.PostMessage(w.hwnd, _WM_CURSOR, 0, 0); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
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) HDC() syscall.Handle {
|
||||
return w.hdc
|
||||
}
|
||||
|
||||
func (w *window) HWND() (syscall.Handle, int, int) {
|
||||
return w.hwnd, w.width, w.height
|
||||
}
|
||||
|
||||
func (w *window) Close() {
|
||||
windows.PostMessage(w.hwnd, windows.WM_CLOSE, 0, 0)
|
||||
}
|
||||
|
||||
func convertKeyCode(code uintptr) (string, bool) {
|
||||
if '0' <= code && code <= '9' || 'A' <= code && code <= 'Z' {
|
||||
return string(rune(code)), true
|
||||
}
|
||||
var r string
|
||||
switch code {
|
||||
case windows.VK_ESCAPE:
|
||||
r = key.NameEscape
|
||||
case windows.VK_LEFT:
|
||||
r = key.NameLeftArrow
|
||||
case windows.VK_RIGHT:
|
||||
r = key.NameRightArrow
|
||||
case windows.VK_RETURN:
|
||||
r = key.NameReturn
|
||||
case windows.VK_UP:
|
||||
r = key.NameUpArrow
|
||||
case windows.VK_DOWN:
|
||||
r = key.NameDownArrow
|
||||
case windows.VK_HOME:
|
||||
r = key.NameHome
|
||||
case windows.VK_END:
|
||||
r = key.NameEnd
|
||||
case windows.VK_BACK:
|
||||
r = key.NameDeleteBackward
|
||||
case windows.VK_DELETE:
|
||||
r = key.NameDeleteForward
|
||||
case windows.VK_PRIOR:
|
||||
r = key.NamePageUp
|
||||
case windows.VK_NEXT:
|
||||
r = key.NamePageDown
|
||||
case windows.VK_F1:
|
||||
r = "F1"
|
||||
case windows.VK_F2:
|
||||
r = "F2"
|
||||
case windows.VK_F3:
|
||||
r = "F3"
|
||||
case windows.VK_F4:
|
||||
r = "F4"
|
||||
case windows.VK_F5:
|
||||
r = "F5"
|
||||
case windows.VK_F6:
|
||||
r = "F6"
|
||||
case windows.VK_F7:
|
||||
r = "F7"
|
||||
case windows.VK_F8:
|
||||
r = "F8"
|
||||
case windows.VK_F9:
|
||||
r = "F9"
|
||||
case windows.VK_F10:
|
||||
r = "F10"
|
||||
case windows.VK_F11:
|
||||
r = "F11"
|
||||
case windows.VK_F12:
|
||||
r = "F12"
|
||||
case windows.VK_TAB:
|
||||
r = key.NameTab
|
||||
case windows.VK_SPACE:
|
||||
r = "Space"
|
||||
case windows.VK_OEM_1:
|
||||
r = ";"
|
||||
case windows.VK_OEM_PLUS:
|
||||
r = "+"
|
||||
case windows.VK_OEM_COMMA:
|
||||
r = ","
|
||||
case windows.VK_OEM_MINUS:
|
||||
r = "-"
|
||||
case windows.VK_OEM_PERIOD:
|
||||
r = "."
|
||||
case windows.VK_OEM_2:
|
||||
r = "/"
|
||||
case windows.VK_OEM_3:
|
||||
r = "`"
|
||||
case windows.VK_OEM_4:
|
||||
r = "["
|
||||
case windows.VK_OEM_5, windows.VK_OEM_102:
|
||||
r = "\\"
|
||||
case windows.VK_OEM_6:
|
||||
r = "]"
|
||||
case windows.VK_OEM_7:
|
||||
r = "'"
|
||||
default:
|
||||
return "", false
|
||||
}
|
||||
return r, true
|
||||
}
|
||||
|
||||
func configForDPI(dpi int) unit.Metric {
|
||||
const inchPrDp = 1.0 / 96.0
|
||||
ppdp := float32(dpi) * inchPrDp
|
||||
return unit.Metric{
|
||||
PxPerDp: ppdp,
|
||||
PxPerSp: ppdp,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user