mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-03 06:20:48 +08:00
Expose screen dimensions (#1519)
* get dimensions working for linux * Cleaning up some GTK code I was getting the following errors due to some bad casts. Gdk-CRITICAL **: 18:58:51.943: gdk_monitor_get_geometry: assertion 'GDK_IS_MONITOR (monitor)' failed Gdk-CRITICAL **: 18:58:51.943: gdk_display_get_monitor_at_window: assertion 'GDK_IS_DISPLAY (display)' failed This commit fixes these errors * Adding Screen namespace along with linux implementation * moving ScreenGetAll into a more appropriate place * Fixing typescript definition mistake, documentation, ordering of functions, and formatting * add ScreenGetAll to more templates * moving screen into its own javascript file * fixing bug where screen objects are not returned from the runtime function * rebuilding frontend wrapper package * adding windows implementation of ScreenGetAll * adding screen get all implementation for darwin * reverting a change that is unrelated to the work on expose-dimensions * removing duplicate comparison * changing GetNthScreen in screen API on macos To use frame instead of visibleframe to keep into account the space the the dock takes up We want to include that space in the calculation in order to keep the sizes of screens consistent across platforms * Correcting screen jsdoc It used to say it returned a single screen object. Now it says that it returns an array of screen objects * Fixing typo in function name * reverting pointless spacing change * reverting pointless spacing change Co-authored-by: Lea Anthony <lea.anthony@gmail.com> Co-authored-by: shmuel.kamensky <shmuel.kamensky@shutterfly.com>
This commit is contained in:
parent
6579cc3ce7
commit
04d35410de
@ -109,6 +109,10 @@ export function WindowSetMinSize(width, height) {
|
|||||||
window.runtime.WindowSetMinSize(width, height);
|
window.runtime.WindowSetMinSize(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ScreenGetAll() {
|
||||||
|
return window.runtime.ScreenGetAll();
|
||||||
|
}
|
||||||
|
|
||||||
export function WindowSetPosition(x, y) {
|
export function WindowSetPosition(x, y) {
|
||||||
window.runtime.WindowSetPosition(x, y);
|
window.runtime.WindowSetPosition(x, y);
|
||||||
}
|
}
|
||||||
|
@ -160,3 +160,7 @@ export function Environment() {
|
|||||||
export function Quit() {
|
export function Quit() {
|
||||||
window.runtime.Quit();
|
window.runtime.Quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ScreenGetAll() {
|
||||||
|
return window.runtime.ScreenGetAll();
|
||||||
|
}
|
@ -160,3 +160,8 @@ export function Environment() {
|
|||||||
export function Quit() {
|
export function Quit() {
|
||||||
window.runtime.Quit();
|
window.runtime.Quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ScreenGetAll() {
|
||||||
|
return window.runtime.ScreenGetAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -160,3 +160,7 @@ export function Environment() {
|
|||||||
export function Quit() {
|
export function Quit() {
|
||||||
window.runtime.Quit();
|
window.runtime.Quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ScreenGetAll() {
|
||||||
|
return window.runtime.ScreenGetAll();
|
||||||
|
}
|
@ -160,3 +160,7 @@ export function Environment() {
|
|||||||
export function Quit() {
|
export function Quit() {
|
||||||
window.runtime.Quit();
|
window.runtime.Quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ScreenGetAll() {
|
||||||
|
return window.runtime.ScreenGetAll();
|
||||||
|
}
|
@ -109,6 +109,10 @@ export function WindowSetMinSize(width, height) {
|
|||||||
window.runtime.WindowSetMinSize(width, height);
|
window.runtime.WindowSetMinSize(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ScreenGetAll() {
|
||||||
|
return window.runtime.ScreenGetAll();
|
||||||
|
}
|
||||||
|
|
||||||
export function WindowSetPosition(x, y) {
|
export function WindowSetPosition(x, y) {
|
||||||
window.runtime.WindowSetPosition(x, y);
|
window.runtime.WindowSetPosition(x, y);
|
||||||
}
|
}
|
||||||
|
@ -117,6 +117,10 @@ export function WindowGetPosition() {
|
|||||||
return window.runtime.WindowGetPosition();
|
return window.runtime.WindowGetPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ScreenGetAll() {
|
||||||
|
return window.runtime.ScreenGetAll();
|
||||||
|
}
|
||||||
|
|
||||||
export function WindowHide() {
|
export function WindowHide() {
|
||||||
window.runtime.WindowHide();
|
window.runtime.WindowHide();
|
||||||
}
|
}
|
||||||
|
@ -160,3 +160,7 @@ export function Environment() {
|
|||||||
export function Quit() {
|
export function Quit() {
|
||||||
window.runtime.Quit();
|
window.runtime.Quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ScreenGetAll() {
|
||||||
|
return window.runtime.ScreenGetAll();
|
||||||
|
}
|
@ -160,3 +160,7 @@ export function Environment() {
|
|||||||
export function Quit() {
|
export function Quit() {
|
||||||
window.runtime.Quit();
|
window.runtime.Quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ScreenGetAll() {
|
||||||
|
return window.runtime.ScreenGetAll();
|
||||||
|
}
|
@ -160,3 +160,7 @@ export function Environment() {
|
|||||||
export function Quit() {
|
export function Quit() {
|
||||||
window.runtime.Quit();
|
window.runtime.Quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ScreenGetAll() {
|
||||||
|
return window.runtime.ScreenGetAll();
|
||||||
|
}
|
@ -160,3 +160,7 @@ export function Environment() {
|
|||||||
export function Quit() {
|
export function Quit() {
|
||||||
window.runtime.Quit();
|
window.runtime.Quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ScreenGetAll() {
|
||||||
|
return window.runtime.ScreenGetAll();
|
||||||
|
}
|
@ -160,3 +160,7 @@ export function Environment() {
|
|||||||
export function Quit() {
|
export function Quit() {
|
||||||
window.runtime.Quit();
|
window.runtime.Quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ScreenGetAll() {
|
||||||
|
return window.runtime.ScreenGetAll();
|
||||||
|
}
|
@ -160,3 +160,7 @@ export function Environment() {
|
|||||||
export function Quit() {
|
export function Quit() {
|
||||||
window.runtime.Quit();
|
window.runtime.Quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ScreenGetAll() {
|
||||||
|
return window.runtime.ScreenGetAll();
|
||||||
|
}
|
@ -224,6 +224,9 @@ func (f *Frontend) WindowSetBackgroundColour(col *options.RGBA) {
|
|||||||
f.mainWindow.SetBackgroundColour(col.R, col.G, col.B, col.A)
|
f.mainWindow.SetBackgroundColour(col.R, col.G, col.B, col.A)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *Frontend) ScreenGetAll() ([]frontend.Screen, error) {
|
||||||
|
return GetAllScreens(f.mainWindow.context)
|
||||||
|
}
|
||||||
func (f *Frontend) Quit() {
|
func (f *Frontend) Quit() {
|
||||||
if f.frontendOptions.OnBeforeClose != nil {
|
if f.frontendOptions.OnBeforeClose != nil {
|
||||||
go func() {
|
go func() {
|
||||||
|
77
v2/internal/frontend/desktop/darwin/screen.go
Normal file
77
v2/internal/frontend/desktop/darwin/screen.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
//go:build darwin
|
||||||
|
// +build darwin
|
||||||
|
|
||||||
|
package darwin
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo CFLAGS: -x objective-c
|
||||||
|
#cgo LDFLAGS: -framework Foundation -framework Cocoa -framework WebKit -framework AppKit
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#include <AppKit/AppKit.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#import "Application.h"
|
||||||
|
#import "WailsContext.h"
|
||||||
|
|
||||||
|
typedef struct Screen {
|
||||||
|
int isCurrent;
|
||||||
|
int isPrimary;
|
||||||
|
int height;
|
||||||
|
int width;
|
||||||
|
} Screen;
|
||||||
|
|
||||||
|
|
||||||
|
int GetNumScreens(){
|
||||||
|
return [[NSScreen screens] count];
|
||||||
|
}
|
||||||
|
|
||||||
|
int screenUniqueID(NSScreen *screen){
|
||||||
|
// adapted from https://stackoverflow.com/a/1237490/4188138
|
||||||
|
NSDictionary* screenDictionary = [screen deviceDescription];
|
||||||
|
NSNumber* screenID = [screenDictionary objectForKey:@"NSScreenNumber"];
|
||||||
|
CGDirectDisplayID aID = [screenID unsignedIntValue];
|
||||||
|
return aID;
|
||||||
|
}
|
||||||
|
|
||||||
|
Screen GetNthScreen(int nth, void *inctx){
|
||||||
|
WailsContext *ctx = (__bridge WailsContext*) inctx;
|
||||||
|
NSArray<NSScreen *> *screens = [NSScreen screens];
|
||||||
|
NSScreen* nthScreen = [screens objectAtIndex:nth];
|
||||||
|
NSScreen* currentScreen = [ctx getCurrentScreen];
|
||||||
|
|
||||||
|
Screen returnScreen;
|
||||||
|
returnScreen.isCurrent = (int)(screenUniqueID(currentScreen)==screenUniqueID(nthScreen));
|
||||||
|
// TODO properly handle screen mirroring
|
||||||
|
// from apple documentation:
|
||||||
|
// https://developer.apple.com/documentation/appkit/nsscreen/1388393-screens?language=objc
|
||||||
|
// The screen at index 0 in the returned array corresponds to the primary screen of the user’s system. This is the screen that contains the menu bar and whose origin is at the point (0, 0). In the case of mirroring, the first screen is the largest drawable display; if all screens are the same size, it is the screen with the highest pixel depth. This primary screen may not be the same as the one returned by the mainScreen method, which returns the screen with the active window.
|
||||||
|
returnScreen.isPrimary = nth==0;
|
||||||
|
returnScreen.height = (int) nthScreen.frame.size.height;
|
||||||
|
returnScreen.width = (int) nthScreen.frame.size.width;
|
||||||
|
return returnScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"github.com/wailsapp/wails/v2/internal/frontend"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetAllScreens(wailsContext unsafe.Pointer) ([]frontend.Screen, error) {
|
||||||
|
err := error(nil)
|
||||||
|
screens := []frontend.Screen{}
|
||||||
|
numScreens := int(C.GetNumScreens())
|
||||||
|
for screeNum := 0; screeNum < numScreens; screeNum++ {
|
||||||
|
screenNumC := C.int(screeNum)
|
||||||
|
cScreen := C.GetNthScreen(screenNumC, wailsContext)
|
||||||
|
screen := frontend.Screen{
|
||||||
|
Height: int(cScreen.height),
|
||||||
|
Width: int(cScreen.width),
|
||||||
|
IsCurrent: cScreen.isCurrent == C.int(1),
|
||||||
|
IsPrimary: cScreen.isPrimary == C.int(1),
|
||||||
|
}
|
||||||
|
screens = append(screens, screen)
|
||||||
|
}
|
||||||
|
return screens, err
|
||||||
|
}
|
@ -216,6 +216,10 @@ func (f *Frontend) WindowSetBackgroundColour(col *options.RGBA) {
|
|||||||
f.mainWindow.SetBackgroundColour(col.R, col.G, col.B, col.A)
|
f.mainWindow.SetBackgroundColour(col.R, col.G, col.B, col.A)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *Frontend) ScreenGetAll() ([]Screen, error) {
|
||||||
|
return GetAllScreens(f.mainWindow.asGTKWindow())
|
||||||
|
}
|
||||||
|
|
||||||
func (f *Frontend) Quit() {
|
func (f *Frontend) Quit() {
|
||||||
if f.frontendOptions.OnBeforeClose != nil {
|
if f.frontendOptions.OnBeforeClose != nil {
|
||||||
go func() {
|
go func() {
|
||||||
|
75
v2/internal/frontend/desktop/linux/screen.go
Normal file
75
v2/internal/frontend/desktop/linux/screen.go
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
//go:build linux
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo linux pkg-config: gtk+-3.0 webkit2gtk-4.0
|
||||||
|
#cgo CFLAGS: -w
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "webkit2/webkit2.h"
|
||||||
|
#include "gtk/gtk.h"
|
||||||
|
#include "gdk/gdk.h"
|
||||||
|
|
||||||
|
typedef struct Screen {
|
||||||
|
int isCurrent;
|
||||||
|
int isPrimary;
|
||||||
|
int height;
|
||||||
|
int width;
|
||||||
|
} Screen;
|
||||||
|
|
||||||
|
int GetNMonitors(GtkWindow *window){
|
||||||
|
GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(window));
|
||||||
|
GdkDisplay *display = gdk_window_get_display(gdk_window);
|
||||||
|
return gdk_display_get_n_monitors(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
Screen GetNThMonitor(int monitor_num, GtkWindow *window){
|
||||||
|
GdkWindow *gdk_window = gtk_widget_get_window(GTK_WIDGET(window));
|
||||||
|
GdkDisplay *display = gdk_window_get_display(gdk_window);
|
||||||
|
GdkMonitor *monitor = gdk_display_get_monitor(display,monitor_num);
|
||||||
|
GdkMonitor *currentMonitor = gdk_display_get_monitor_at_window(display,gdk_window);
|
||||||
|
Screen screen;
|
||||||
|
GdkRectangle geometry;
|
||||||
|
gdk_monitor_get_geometry(monitor,&geometry);
|
||||||
|
screen.isCurrent = currentMonitor==monitor;
|
||||||
|
screen.isPrimary = gdk_monitor_is_primary(monitor);
|
||||||
|
screen.height = geometry.height;
|
||||||
|
screen.width = geometry.width;
|
||||||
|
return screen;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
import (
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/frontend"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Screen = frontend.Screen
|
||||||
|
|
||||||
|
func GetAllScreens(window *C.GtkWindow) ([]Screen, error) {
|
||||||
|
if window == nil {
|
||||||
|
return nil, errors.New("window is nil, cannot perform screen operations")
|
||||||
|
}
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
var screens []Screen
|
||||||
|
wg.Add(1)
|
||||||
|
invokeOnMainThread(func() {
|
||||||
|
numMonitors := C.GetNMonitors(window)
|
||||||
|
for i := 0; i < int(numMonitors); i++ {
|
||||||
|
cMonitor := C.GetNThMonitor(C.int(i), window)
|
||||||
|
screen := Screen{
|
||||||
|
IsCurrent: cMonitor.isCurrent == 1,
|
||||||
|
IsPrimary: cMonitor.isPrimary == 1,
|
||||||
|
Width: int(cMonitor.width),
|
||||||
|
Height: int(cMonitor.height),
|
||||||
|
}
|
||||||
|
screens = append(screens, screen)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Done()
|
||||||
|
})
|
||||||
|
wg.Wait()
|
||||||
|
return screens, nil
|
||||||
|
}
|
@ -431,20 +431,12 @@ typedef struct RGBAOptions {
|
|||||||
uint8_t b;
|
uint8_t b;
|
||||||
uint8_t a;
|
uint8_t a;
|
||||||
void *webview;
|
void *webview;
|
||||||
void *window;
|
|
||||||
} RGBAOptions;
|
} RGBAOptions;
|
||||||
|
|
||||||
void setBackgroundColour(void* data) {
|
void setBackgroundColour(void* data) {
|
||||||
RGBAOptions* options = (RGBAOptions*)data;
|
RGBAOptions* options = (RGBAOptions*)data;
|
||||||
GdkRGBA colour = {options->r / 255.0, options->g / 255.0, options->b / 255.0, options->a / 255.0};
|
GdkRGBA colour = {options->r / 255.0, options->g / 255.0, options->b / 255.0, options->a / 255.0};
|
||||||
webkit_web_view_set_background_color(WEBKIT_WEB_VIEW(options->webview), &colour);
|
webkit_web_view_set_background_color(WEBKIT_WEB_VIEW(options->webview), &colour);
|
||||||
GtkCssProvider *provider = gtk_css_provider_new();
|
|
||||||
char buffer[60];
|
|
||||||
sprintf((void*)&buffer[0], "* { background-color: rgba(%d,%d,%d,%d); }", options->r, options->g, options->b, options->a);
|
|
||||||
gtk_css_provider_load_from_data (provider, &buffer[0], -1, NULL);
|
|
||||||
gtk_style_context_add_provider(gtk_widget_get_style_context(GTK_WIDGET(options->window)),
|
|
||||||
GTK_STYLE_PROVIDER(provider),
|
|
||||||
GTK_STYLE_PROVIDER_PRIORITY_USER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct SetTitleArgs {
|
typedef struct SetTitleArgs {
|
||||||
@ -794,7 +786,6 @@ func (w *Window) SetBackgroundColour(r uint8, g uint8, b uint8, a uint8) {
|
|||||||
b: C.uchar(b),
|
b: C.uchar(b),
|
||||||
a: C.uchar(a),
|
a: C.uchar(a),
|
||||||
webview: w.webview,
|
webview: w.webview,
|
||||||
window: w.gtkWindow,
|
|
||||||
}
|
}
|
||||||
invokeOnMainThread(func() { C.setBackgroundColour(unsafe.Pointer(&data)) })
|
invokeOnMainThread(func() { C.setBackgroundColour(unsafe.Pointer(&data)) })
|
||||||
|
|
||||||
|
@ -27,12 +27,15 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const startURL = "http://wails.localhost/"
|
const startURL = "http://wails.localhost/"
|
||||||
|
|
||||||
|
type Screen = frontend.Screen
|
||||||
|
|
||||||
type Frontend struct {
|
type Frontend struct {
|
||||||
|
|
||||||
// Context
|
// Context
|
||||||
@ -340,6 +343,20 @@ func (f *Frontend) WindowSetBackgroundColour(col *options.RGBA) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *Frontend) ScreenGetAll() ([]Screen, error) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(1)
|
||||||
|
screens := []Screen{}
|
||||||
|
err := error(nil)
|
||||||
|
f.mainWindow.Invoke(func() {
|
||||||
|
screens, err = GetAllScreens(f.mainWindow.Handle())
|
||||||
|
wg.Done()
|
||||||
|
|
||||||
|
})
|
||||||
|
wg.Wait()
|
||||||
|
return screens, err
|
||||||
|
}
|
||||||
|
|
||||||
func (f *Frontend) Quit() {
|
func (f *Frontend) Quit() {
|
||||||
if f.frontendOptions.OnBeforeClose != nil && f.frontendOptions.OnBeforeClose(f.ctx) {
|
if f.frontendOptions.OnBeforeClose != nil && f.frontendOptions.OnBeforeClose(f.ctx) {
|
||||||
return
|
return
|
||||||
|
116
v2/internal/frontend/desktop/windows/screen.go
Normal file
116
v2/internal/frontend/desktop/windows/screen.go
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package windows
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/winc/w32"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MonitorsEqual(first w32.MONITORINFO, second w32.MONITORINFO) bool {
|
||||||
|
// Checks to make sure all the fields are the same.
|
||||||
|
// A cleaner way would be to check identity of devices. but I couldn't find a way of doing that using the win32 API
|
||||||
|
return first.DwFlags == second.DwFlags &&
|
||||||
|
first.RcMonitor.Top == second.RcMonitor.Top &&
|
||||||
|
first.RcMonitor.Bottom == second.RcMonitor.Bottom &&
|
||||||
|
first.RcMonitor.Right == second.RcMonitor.Right &&
|
||||||
|
first.RcMonitor.Left == second.RcMonitor.Left &&
|
||||||
|
first.RcWork.Top == second.RcWork.Top &&
|
||||||
|
first.RcWork.Bottom == second.RcWork.Bottom &&
|
||||||
|
first.RcWork.Right == second.RcWork.Right &&
|
||||||
|
first.RcWork.Left == second.RcWork.Left
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMonitorInfo(hMonitor w32.HMONITOR) (*w32.MONITORINFO, error) {
|
||||||
|
// Adapted from winc.utils.getMonitorInfo TODO: add this to win32
|
||||||
|
// See docs for
|
||||||
|
//https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmonitorinfoa
|
||||||
|
|
||||||
|
var info w32.MONITORINFO
|
||||||
|
info.CbSize = uint32(unsafe.Sizeof(info))
|
||||||
|
succeeded := w32.GetMonitorInfo(hMonitor, &info)
|
||||||
|
if !succeeded {
|
||||||
|
return &info, errors.New("Windows call to getMonitorInfo failed")
|
||||||
|
}
|
||||||
|
return &info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func EnumProc(hMonitor w32.HMONITOR, hdcMonitor w32.HDC, lprcMonitor *w32.RECT, dwData w32.LPARAM) uintptr {
|
||||||
|
// adapted from https://stackoverflow.com/a/23492886/4188138
|
||||||
|
|
||||||
|
// see docs for the following pages to better understand this function
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaymonitors
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nc-winuser-monitorenumproc
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-monitorinfo
|
||||||
|
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-monitorfromwindow
|
||||||
|
|
||||||
|
ourMonitorData := Screen{}
|
||||||
|
screenContainer := (*ScreenContainer)(unsafe.Pointer(dwData))
|
||||||
|
|
||||||
|
currentMonHndl := w32.MonitorFromWindow(screenContainer.mainWinHandle, w32.MONITOR_DEFAULTTONEAREST)
|
||||||
|
currentMonInfo, currErr := GetMonitorInfo(currentMonHndl)
|
||||||
|
|
||||||
|
if currErr != nil {
|
||||||
|
screenContainer.errors = append(screenContainer.errors, currErr)
|
||||||
|
screenContainer.monitors = append(screenContainer.monitors, Screen{})
|
||||||
|
// not sure what the consequences of returning false are, so let's just return true and handle it ourselves
|
||||||
|
return w32.TRUE
|
||||||
|
}
|
||||||
|
|
||||||
|
monInfo, err := GetMonitorInfo(hMonitor)
|
||||||
|
if err != nil {
|
||||||
|
screenContainer.errors = append(screenContainer.errors, err)
|
||||||
|
screenContainer.monitors = append(screenContainer.monitors, Screen{})
|
||||||
|
return w32.TRUE
|
||||||
|
}
|
||||||
|
|
||||||
|
height := lprcMonitor.Right - lprcMonitor.Left
|
||||||
|
width := lprcMonitor.Bottom - lprcMonitor.Top
|
||||||
|
ourMonitorData.IsPrimary = monInfo.DwFlags&w32.MONITORINFOF_PRIMARY == 1
|
||||||
|
ourMonitorData.Height = int(width)
|
||||||
|
ourMonitorData.Width = int(height)
|
||||||
|
ourMonitorData.IsCurrent = MonitorsEqual(*currentMonInfo, *monInfo)
|
||||||
|
|
||||||
|
// the reason we need a container is that we have don't know how many times this function will be called
|
||||||
|
// this "append" call could potentially do an allocation and rewrite the pointer to monitors. So we save the pointer in screenContainer.monitors
|
||||||
|
// and retrieve the values after all EnumProc calls
|
||||||
|
// If EnumProc is multi-threaded, this could be problematic. Although, I don't think it is.
|
||||||
|
screenContainer.monitors = append(screenContainer.monitors, ourMonitorData)
|
||||||
|
// let's keep screenContainer.errors the same size as screenContainer.monitors in case we want to match them up later if necessary
|
||||||
|
screenContainer.errors = append(screenContainer.errors, nil)
|
||||||
|
return w32.TRUE
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScreenContainer struct {
|
||||||
|
monitors []Screen
|
||||||
|
errors []error
|
||||||
|
mainWinHandle w32.HWND
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAllScreens(mainWinHandle w32.HWND) ([]Screen, error) {
|
||||||
|
// TODO fix hack of container sharing by having a proper data sharing mechanism between windows and the runtime
|
||||||
|
monitorContainer := ScreenContainer{mainWinHandle: mainWinHandle}
|
||||||
|
returnErr := error(nil)
|
||||||
|
errorStrings := []string{}
|
||||||
|
|
||||||
|
dc := w32.GetDC(0)
|
||||||
|
defer w32.ReleaseDC(0, dc)
|
||||||
|
succeeded := w32.EnumDisplayMonitors(dc, nil, syscall.NewCallback(EnumProc), uintptr(unsafe.Pointer(&monitorContainer)))
|
||||||
|
if !succeeded {
|
||||||
|
return monitorContainer.monitors, errors.New("Windows call to EnumDisplayMonitors failed")
|
||||||
|
}
|
||||||
|
for idx, err := range monitorContainer.errors {
|
||||||
|
if err != nil {
|
||||||
|
errorStrings = append(errorStrings, fmt.Sprintf("Error from monitor #%v, %v", idx+1, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errorStrings) > 0 {
|
||||||
|
returnErr = fmt.Errorf("%v errors encountered: %v", len(errorStrings), errorStrings)
|
||||||
|
}
|
||||||
|
return monitorContainer.monitors, returnErr
|
||||||
|
}
|
@ -28,6 +28,8 @@ import (
|
|||||||
"golang.org/x/net/websocket"
|
"golang.org/x/net/websocket"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Screen = frontend.Screen
|
||||||
|
|
||||||
type DevWebServer struct {
|
type DevWebServer struct {
|
||||||
server *echo.Echo
|
server *echo.Echo
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
@ -247,6 +249,10 @@ func (d *DevWebServer) WindowSetBackgroundColour(col *options.RGBA) {
|
|||||||
d.desktopFrontend.WindowSetBackgroundColour(col)
|
d.desktopFrontend.WindowSetBackgroundColour(col)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *DevWebServer) ScreenGetAll() ([]Screen, error) {
|
||||||
|
return d.desktopFrontend.ScreenGetAll()
|
||||||
|
}
|
||||||
|
|
||||||
func (d *DevWebServer) MenuSetApplicationMenu(menu *menu.Menu) {
|
func (d *DevWebServer) MenuSetApplicationMenu(menu *menu.Menu) {
|
||||||
d.desktopFrontend.MenuSetApplicationMenu(menu)
|
d.desktopFrontend.MenuSetApplicationMenu(menu)
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,8 @@ func (d *Dispatcher) processSystemCall(payload callMessage, sender frontend.Fron
|
|||||||
case "WindowGetSize":
|
case "WindowGetSize":
|
||||||
w, h := sender.WindowGetSize()
|
w, h := sender.WindowGetSize()
|
||||||
return &size{w, h}, nil
|
return &size{w, h}, nil
|
||||||
|
case "ScreenGetAll":
|
||||||
|
return sender.ScreenGetAll()
|
||||||
case "Environment":
|
case "Environment":
|
||||||
return runtime.Environment(d.ctx), nil
|
return runtime.Environment(d.ctx), nil
|
||||||
default:
|
default:
|
||||||
|
@ -45,6 +45,13 @@ const (
|
|||||||
QuestionDialog DialogType = "question"
|
QuestionDialog DialogType = "question"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Screen struct {
|
||||||
|
IsCurrent bool `json:"isCurrent"`
|
||||||
|
IsPrimary bool `json:"isPrimary"`
|
||||||
|
Width int `json:"width"`
|
||||||
|
Height int `json:"height"`
|
||||||
|
}
|
||||||
|
|
||||||
// MessageDialogOptions contains the options for the Message dialogs, EG Info, Warning, etc runtime methods
|
// MessageDialogOptions contains the options for the Message dialogs, EG Info, Warning, etc runtime methods
|
||||||
type MessageDialogOptions struct {
|
type MessageDialogOptions struct {
|
||||||
Type DialogType
|
Type DialogType
|
||||||
@ -93,6 +100,9 @@ type Frontend interface {
|
|||||||
WindowSetLightTheme()
|
WindowSetLightTheme()
|
||||||
WindowSetDarkTheme()
|
WindowSetDarkTheme()
|
||||||
|
|
||||||
|
//Screen
|
||||||
|
ScreenGetAll() ([]Screen, error)
|
||||||
|
|
||||||
// Menus
|
// Menus
|
||||||
MenuSetApplicationMenu(menu *menu.Menu)
|
MenuSetApplicationMenu(menu *menu.Menu)
|
||||||
MenuUpdateApplicationMenu()
|
MenuUpdateApplicationMenu()
|
||||||
|
@ -13,6 +13,7 @@ import {eventListeners, EventsEmit, EventsNotify, EventsOff, EventsOn, EventsOnc
|
|||||||
import {Call, Callback, callbacks} from './calls';
|
import {Call, Callback, callbacks} from './calls';
|
||||||
import {SetBindings} from "./bindings";
|
import {SetBindings} from "./bindings";
|
||||||
import * as Window from "./window";
|
import * as Window from "./window";
|
||||||
|
import * as Screen from "./screen";
|
||||||
import * as Browser from "./browser";
|
import * as Browser from "./browser";
|
||||||
|
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ window.runtime = {
|
|||||||
...Log,
|
...Log,
|
||||||
...Window,
|
...Window,
|
||||||
...Browser,
|
...Browser,
|
||||||
|
...Screen,
|
||||||
EventsOn,
|
EventsOn,
|
||||||
EventsOnce,
|
EventsOnce,
|
||||||
EventsOnMultiple,
|
EventsOnMultiple,
|
||||||
|
25
v2/internal/frontend/runtime/desktop/screen.js
Normal file
25
v2/internal/frontend/runtime/desktop/screen.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
_ __ _ __
|
||||||
|
| | / /___ _(_) /____
|
||||||
|
| | /| / / __ `/ / / ___/
|
||||||
|
| |/ |/ / /_/ / / (__ )
|
||||||
|
|__/|__/\__,_/_/_/____/
|
||||||
|
The electron alternative for Go
|
||||||
|
(c) Lea Anthony 2019-present
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* jshint esversion: 9 */
|
||||||
|
|
||||||
|
|
||||||
|
import {Call} from "./calls";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the all screens. Call this anew each time you want to refresh data from the underlying windowing system.
|
||||||
|
* @export
|
||||||
|
* @typedef {import('../wrapper/runtime').Screen} Screen
|
||||||
|
* @return {Promise<{Screen[]}>} The screens
|
||||||
|
*/
|
||||||
|
export function ScreenGetAll() {
|
||||||
|
return Call(":wails:ScreenGetAll");
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -18,6 +18,13 @@ export interface Size {
|
|||||||
h: number;
|
h: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Screen {
|
||||||
|
isCurrent: boolean;
|
||||||
|
isPrimary: boolean;
|
||||||
|
width : number
|
||||||
|
height : number
|
||||||
|
}
|
||||||
|
|
||||||
// Environment information such as platform, buildtype, ...
|
// Environment information such as platform, buildtype, ...
|
||||||
export interface EnvironmentInfo {
|
export interface EnvironmentInfo {
|
||||||
buildType: string;
|
buildType: string;
|
||||||
@ -171,6 +178,10 @@ export function WindowUnminimise(): void;
|
|||||||
// Sets the background colour of the window to the given RGBA colour definition. This colour will show through for all transparent pixels.
|
// Sets the background colour of the window to the given RGBA colour definition. This colour will show through for all transparent pixels.
|
||||||
export function WindowSetBackgroundColour(R: number, G: number, B: number, A: number): void;
|
export function WindowSetBackgroundColour(R: number, G: number, B: number, A: number): void;
|
||||||
|
|
||||||
|
// [ScreenGetAll](https://wails.io/docs/reference/runtime/window#screengetall)
|
||||||
|
// Gets the all screens. Call this anew each time you want to refresh data from the underlying windowing system.
|
||||||
|
export function ScreenGetAll(): Promise<Screen[]>;
|
||||||
|
|
||||||
// [BrowserOpenURL](https://wails.io/docs/reference/runtime/browser#browseropenurl)
|
// [BrowserOpenURL](https://wails.io/docs/reference/runtime/browser#browseropenurl)
|
||||||
// Opens the given URL in the system browser.
|
// Opens the given URL in the system browser.
|
||||||
export function BrowserOpenURL(url: string): void;
|
export function BrowserOpenURL(url: string): void;
|
||||||
|
@ -149,6 +149,10 @@ export function WindowSetBackgroundColour(R, G, B, A) {
|
|||||||
window.runtime.WindowSetBackgroundColour(R, G, B, A);
|
window.runtime.WindowSetBackgroundColour(R, G, B, A);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ScreenGetAll() {
|
||||||
|
return window.runtime.ScreenGetAll();
|
||||||
|
}
|
||||||
|
|
||||||
export function BrowserOpenURL(url) {
|
export function BrowserOpenURL(url) {
|
||||||
window.runtime.BrowserOpenURL(url);
|
window.runtime.BrowserOpenURL(url);
|
||||||
}
|
}
|
||||||
|
12
v2/pkg/runtime/screen.go
Normal file
12
v2/pkg/runtime/screen.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
import "github.com/wailsapp/wails/v2/internal/frontend"
|
||||||
|
|
||||||
|
type Screen = frontend.Screen
|
||||||
|
|
||||||
|
// ScreenGetAllScreens returns all screens
|
||||||
|
func ScreenGetAll(ctx context.Context) ([]Screen, error) {
|
||||||
|
appFrontend := getFrontend(ctx)
|
||||||
|
return appFrontend.ScreenGetAll()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user