zorldo

Goofing around with Ebiten
git clone git://bsandro.tech/zorldo
Log | Files | Refs | README

ui_unix.go (5740B)


      1 // Copyright 2016 Hajime Hoshi
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 //go:build (dragonfly || freebsd || linux || netbsd || openbsd || solaris) && !android
     16 // +build dragonfly freebsd linux netbsd openbsd solaris
     17 // +build !android
     18 
     19 package glfw
     20 
     21 import (
     22 	"fmt"
     23 	"math"
     24 	"runtime"
     25 
     26 	"github.com/hajimehoshi/ebiten/v2/internal/driver"
     27 	"github.com/hajimehoshi/ebiten/v2/internal/glfw"
     28 	"github.com/jezek/xgb"
     29 	"github.com/jezek/xgb/randr"
     30 	"github.com/jezek/xgb/xproto"
     31 )
     32 
     33 type videoModeScaleCacheKey struct{ X, Y int }
     34 
     35 var videoModeScaleCache = map[videoModeScaleCacheKey]float64{}
     36 
     37 // clearVideoModeScaleCache must be called from the main thread.
     38 func clearVideoModeScaleCache() {
     39 	for k := range videoModeScaleCache {
     40 		delete(videoModeScaleCache, k)
     41 	}
     42 }
     43 
     44 // videoModeScale must be called from the main thread.
     45 func videoModeScale(m *glfw.Monitor) float64 {
     46 	// Caching wrapper for videoModeScaleUncached as
     47 	// videoModeScaleUncached may be expensive (uses blocking calls on X connection)
     48 	// and public ScreenSizeInFullscreen API needs the videoModeScale.
     49 	monitorX, monitorY := m.GetPos()
     50 	cacheKey := videoModeScaleCacheKey{X: monitorX, Y: monitorY}
     51 	if cached, ok := videoModeScaleCache[cacheKey]; ok {
     52 		return cached
     53 	}
     54 
     55 	scale := videoModeScaleUncached(m)
     56 	videoModeScaleCache[cacheKey] = scale
     57 	return scale
     58 }
     59 
     60 // videoModeScaleUncached must be called from the main thread.
     61 func videoModeScaleUncached(m *glfw.Monitor) float64 {
     62 	// TODO: if glfw/glfw#1961 gets fixed, this function may need revising.
     63 	// In case GLFW decides to switch to returning logical pixels, we can just return 1.
     64 
     65 	// Note: GLFW currently returns physical pixel sizes,
     66 	// but we need to predict the window system-side size of the fullscreen window
     67 	// for Ebiten's `ScreenSizeInFullscreen` public API.
     68 	// Also at the moment we need this prior to switching to fullscreen, but that might be replacable.
     69 	// So this function computes the ratio of physical per logical pixels.
     70 	xconn, err := xgb.NewConn()
     71 	if err != nil {
     72 		// No X11 connection?
     73 		// Assume we're on pure Wayland then.
     74 		// GLFW/Wayland shouldn't be having this issue.
     75 		return 1
     76 	}
     77 	defer xconn.Close()
     78 
     79 	if err := randr.Init(xconn); err != nil {
     80 		// No RANDR extension? No problem.
     81 		return 1
     82 	}
     83 
     84 	root := xproto.Setup(xconn).DefaultScreen(xconn).Root
     85 	res, err := randr.GetScreenResourcesCurrent(xconn, root).Reply()
     86 	if err != nil {
     87 		// Likely means RANDR is not working. No problem.
     88 		return 1
     89 	}
     90 
     91 	monitorX, monitorY := m.GetPos()
     92 
     93 	for _, crtc := range res.Crtcs[:res.NumCrtcs] {
     94 		info, err := randr.GetCrtcInfo(xconn, crtc, res.ConfigTimestamp).Reply()
     95 		if err != nil {
     96 			// This Crtc is bad. Maybe just got disconnected?
     97 			continue
     98 		}
     99 		if info.NumOutputs == 0 {
    100 			// This Crtc is not connected to any output.
    101 			// In other words, a disabled monitor.
    102 			continue
    103 		}
    104 		if int(info.X) == monitorX && int(info.Y) == monitorY {
    105 			xWidth, xHeight := info.Width, info.Height
    106 			vm := m.GetVideoMode()
    107 			physWidth, physHeight := vm.Width, vm.Height
    108 			// Return one scale, even though there may be separate X and Y scales.
    109 			// Return the _larger_ scale, as this would yield a letterboxed display on mismatch, rather than a cut-off one.
    110 			scale := math.Max(float64(physWidth)/float64(xWidth), float64(physHeight)/float64(xHeight))
    111 			return scale
    112 		}
    113 	}
    114 
    115 	// Monitor not known to XRandR. Weird.
    116 	return 1
    117 }
    118 
    119 // fromGLFWMonitorPixel must be called from the main thread.
    120 func (u *UserInterface) fromGLFWMonitorPixel(x float64, videoModeScale float64) float64 {
    121 	return x / (videoModeScale * u.deviceScaleFactor())
    122 }
    123 
    124 // fromGLFWPixel must be called from the main thread.
    125 func (u *UserInterface) fromGLFWPixel(x float64) float64 {
    126 	return x / u.deviceScaleFactor()
    127 }
    128 
    129 // toGLFWPixel must be called from the main thread.
    130 func (u *UserInterface) toGLFWPixel(x float64) float64 {
    131 	return x * u.deviceScaleFactor()
    132 }
    133 
    134 func (u *UserInterface) adjustWindowPosition(x, y int) (int, int) {
    135 	return x, y
    136 }
    137 
    138 func currentMonitorByOS(_ *glfw.Window) *glfw.Monitor {
    139 	// TODO: Implement this correctly. (#1119).
    140 	return nil
    141 }
    142 
    143 func (u *UserInterface) nativeWindow() uintptr {
    144 	// TODO: Implement this.
    145 	return 0
    146 }
    147 
    148 func (u *UserInterface) isNativeFullscreen() bool {
    149 	return false
    150 }
    151 
    152 func (u *UserInterface) setNativeCursor(shape driver.CursorShape) {
    153 	// TODO: Use native API in the future (#1571)
    154 	u.window.SetCursor(glfwSystemCursors[shape])
    155 }
    156 
    157 func (u *UserInterface) isNativeFullscreenAvailable() bool {
    158 	return false
    159 }
    160 
    161 func (u *UserInterface) setNativeFullscreen(fullscreen bool) {
    162 	panic(fmt.Sprintf("glfw: setNativeFullscreen is not implemented in this environment: %s", runtime.GOOS))
    163 }
    164 
    165 func (u *UserInterface) adjustViewSize() {
    166 }
    167 
    168 func initializeWindowAfterCreation(w *glfw.Window) {
    169 	// Show the window once before getting the position of the window.
    170 	// On Linux/Unix, the window position is not reliable before showing.
    171 	w.Show()
    172 
    173 	// Hiding the window makes the position unreliable again. Do not call w.Hide() here (#1829)
    174 	// Calling Hide is problematic especially on XWayland and/or Sway.
    175 	// Apparently the window state is inconsistent just after the window is created, but we are not sure.
    176 	// For more details, see the discussion in #1829.
    177 }