Merge pull request #67 from chaosddp/screen_related

Query supported resolutions and other display info.
Fix a number of bugs
Clean up docs
master
Marius Petcu 2018-10-26 00:51:10 +03:00 committed by GitHub
commit 6c4c406211
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 2461 additions and 289 deletions

192
README.md
View File

@ -1,27 +1,31 @@
# DefOS
Extra native OS functions for games written using the Defold game engine
Currently uses Native Extension for macOS, Windows and HTML5. Contribute!
Currently supports macOS, Windows, Linux and HTML5. Contribute!
## Installation
You can use DefOS in your own project by adding this project as a [Defold library dependency](http://www.defold.com/manuals/libraries/). Open your game.project file and in the dependencies field under project add:
You can use DefOS in your own project by adding this project as a
[Defold library dependency](http://www.defold.com/manuals/libraries/).
Open your `game.project` file and in the dependencies field under project add:
https://github.com/subsoap/defos/archive/master.zip
## Methods
Customize title bar accessories and title.
**Customize title bar** accessories and title.
```lua
defos.disable_maximize_button()
defos.disable_minimize_button()
defos.disable_window_resize()
defos.disable_maximize_button() -- Not supported on HTML5
defos.disable_minimize_button() -- Not supported on HTML5
defos.disable_window_resize() -- Not supported on HTML5
defos.set_window_title("I set this title using Defos")
```
---
Toggle window maximize status.
**Toggle window maximize** status.
```lua
defos.set_maximized(bool_value)
@ -31,7 +35,7 @@ bool_value = defos.is_maximized()
---
Toggle full screen. On HTML5, this only works from `defos.on_click()`.
**Toggle full screen**. On HTML5, this only works from `defos.on_click()`.
```lua
defos.set_fullscreen(bool_value)
@ -41,7 +45,25 @@ bool_value = defos.is_fullscreen()
---
Get/set the window's size and position in screen coordinates. The window area
**Keep window on top**. Not supported on HTML5.
```lua
defos.set_always_on_top(bool_value)
defos.toggle_always_on_top()
bool_value = defos.is_always_on_top()
```
---
**Minimize window**. Not supported on HTML5.
```lua
defos.minimize()
```
---
**Get/set the window's size and position** in screen coordinates. The window area
includes the title bar, so the actual contained game view area might be smaller
than the given metrics.
@ -57,7 +79,7 @@ defos.set_window_size(x, y, w, h)
---
Get/set the game view size and position in screen coordinates. This adjusts
**Get/set the game view size and position** in screen coordinates. This adjusts
the window so that its containing game view is at the desired size and position.
The window will be larger than the given metrics because it includes the title
bar.
@ -71,7 +93,75 @@ defos.set_view_size(x, y, w, h)
---
Show/hide the mouse cursor.
**Query displays**.
`defos.get_displays()` returns a table which can be indexed with either number
indices (like an array), either with display `id`s.
Not supported on HTML5.
```lua
displays = defos.get_displays()
pprint(displays[1]) -- Print info about the main display
current_display_id = defos.get_current_display_id() -- Get the ID of the game's current display
pprint(displays[current_display_id]) -- Print info about the game's current display
```
A display info table has the following format:
```lua
{
id = <userdata>,
bounds = { -- This is the position and size in screen coordinates of the
x = 0, -- display (relative to the top-left corner of the main display)
y = 0,
width = 1440,
height = 900,
}
mode = { -- The current resolution mode of the display
width = 2880,
height = 1800,
scaling_factor = 2,
refresh_rate = 60,
bits_per_pixel = 32,
orientation = 0,
reflect_x = false,
reflect_y = false,
},
name = "Built-in Retina Display",
}
```
---
**Query resolution modes** for a display.
Returns a table with all the resolution modes a display supports.
Not supported on HTML5.
```lua
display_id = defos.get_current_display_id()
modes = defos.get_display_modes(display_id)
pprint(modes[1]) -- Print information about the first available resolution mode
```
A resolution mode has the following format:
```lua
{
width = 2880, -- Full width/height in pixels (not points)
height = 1800,
scaling_factor = 2, -- Hi-DPI scaling factor
refresh_rate = 60,
bits_per_pixel = 32,
orientation = 0, -- One of 0, 90, 180, 270 (degrees measured clockwise)
reflect_x = false, -- Linux supports reflecting either of the axes,
reflect_y = false, -- effectively flipping the image like a mirror
}
```
---
**Show/hide the mouse cursor.**
```lua
defos.set_cursor_visible(bool_value)
@ -80,7 +170,9 @@ bool_value = defos.is_cursor_visible()
---
Respond to the mouse entering and leaving the game view area.
**Respond to the mouse entering and leaving** the game view area.
`on_mouse_enter()` / `on_mouse_leave()` not supported on Linux yet.
```lua
bool_value = defos.is_mouse_in_view()
@ -94,16 +186,28 @@ end)
---
Move the cursor programatically.
**Get the cursor position**.
```lua
defos.set_cursor_pos(x, y) -- In screen coordinates
defos.move_cursor_to(x, y) -- In game view coordinates
x, y = defos.get_cursor_pos() -- In screen coordinates
x, y = defos.get_cursor_pos_view() -- In game view coordinates
```
---
Clip cursor to current game view area. Windows only.
**Move the cursor** programatically.
Not supported on HTML5.
```lua
defos.set_cursor_pos(x, y) -- In screen coordinates
defos.set_cursor_pos_view(x, y) -- In game view coordinates
```
---
**Clip cursor** to current game view area.
Not supported on Linux and HTML5.
```lua
defos.set_cursor_clipped(bool_value)
@ -112,7 +216,10 @@ bool_value = defos.is_cursor_clipped()
---
Lock cursor movement. On HTML5 this only works from `defos.on_click()`.
**Lock cursor movement**.
On HTML5 this only works from `defos.on_click()`.
Not supported on Linux yet.
```lua
defos.set_cursor_locked(bool_value)
@ -124,7 +231,7 @@ end)
---
Set custom hardware cursors. `cursor` can be one of the following:
**Set custom hardware cursors**. `cursor` can be one of the following:
* `nil`: Resets the cursor to default. Equivalent to `defos.reset_cursor()`.
* `defos.CURSOR_ARROW`
* `defos.CURSOR_HAND`
@ -141,11 +248,14 @@ Set custom hardware cursors. `cursor` can be one of the following:
}
```
On macOS, custom cursors can be any image file supported by `NSImage`, but it's highly recommended to
[create a TIFF](https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Optimizing/Optimizing.html#//apple_ref/doc/uid/TP40012302-CH7-SW27)
with two images, one at 72DPI (for low density displays) and another at 144DPI (for Retina displays).
On macOS, custom cursors can be any image file supported by `NSImage`, but it's
highly recommended to [create a TIFF][cursor-tiff] with two images, one at
72DPI (for low density displays) and another at 144DPI (for Retina displays).
The hotspot is an anchor point within the image that will overlap with the functional position of the mouse pointer (eg. the tip of the arrow).
The hotspot is an anchor point within the image that will overlap with the
functional position of the mouse pointer (eg. the tip of the arrow).
[cursor-tiff]: https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Optimizing/Optimizing.html#//apple_ref/doc/uid/TP40012302-CH7-SW27
```lua
defos.set_cursor(cursor)
@ -154,7 +264,7 @@ defos.reset_cursor()
---
On Windows only, show/hide the console window. Only works when not running
**Show/hide the console window** on Windows. Only works when not running
from the Editor.
```lua
@ -164,7 +274,7 @@ bool_value = defos.is_console_visible()
---
On HTML5 only, get a synchronous event when the user clicks in the canvas.
On HTML5 only, **get a synchronous event when the user clicks** in the canvas.
This is necessary because some HTML5 functions only work when called
synchronously from an event handler.
@ -180,7 +290,9 @@ end)
---
Get the absolute path to the game's containing directory. On macOS this will be the path to the .app bundle
**Get the absolute path to the game's containing directory**. On macOS this
will be the path to the .app bundle. On HTML5 this will be the page URL up until
the last `/`.
```lua
path = defos.get_bundle_root()
@ -188,32 +300,38 @@ path = defos.get_bundle_root()
---
The system path separator. `"\\"` on Windows, `"/"` everywhere else.
**The system path separator.** `"\\"` on Windows, `"/"` everywhere else.
```lua
defos.PATH_SEP
```
---
Change the game's icon at runtime.
```lua
defos.set_window_icon(path_to_icon)
```
---
Returns a table of command line arguments used to run the app. On HTML5, returns a table with a single string: the query string part of the URL (eg. `{ "?param1=foo&param2=bar" }`).
**Change the game window's icon** at runtime. On Windows, this function accepts
`.ico` files. On macOS this accepts any image file supported by `NSImage`.
On Linux this function is not supported yet.
```lua
arguments = defos.get_parameters()
defos.set_window_icon(path_to_icon_file)
```
---
If you'd like to see any other feature, open an issue.
**Returns a table of command line arguments** used to run the app. On HTML5,
returns a table with a single string: the query string part of the URL
(eg. `{ "?param1=foo&param2=bar" }`).
```lua
arguments = defos.get_arguments()
```
---
If you'd like to see any other features, open an issue.
## Example
An example is made using [DirtyLarry](https://github.com/andsve/dirtylarry)
![Defos example screenshot](https://user-images.githubusercontent.com/2209596/37050119-158e6b34-2184-11e8-95fd-b2e293fba456.jpg)

590
defos/include/Xrandr.h Normal file
View File

@ -0,0 +1,590 @@
/*
* Copyright © 2000 Compaq Computer Corporation, Inc.
* Copyright © 2002 Hewlett-Packard Company, Inc.
* Copyright © 2006 Intel Corporation
* Copyright © 2008 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*
* Author: Jim Gettys, HP Labs, Hewlett-Packard, Inc.
* Keith Packard, Intel Corporation
*/
#if defined(DM_PLATFORM_LINUX)
#ifndef _XRANDR_H_
#define _XRANDR_H_
#include <randr.h>
#include <X11/extensions/Xrender.h>
#include <X11/Xfuncproto.h>
_XFUNCPROTOBEGIN
typedef XID RROutput;
typedef XID RRCrtc;
typedef XID RRMode;
typedef XID RRProvider;
typedef struct {
int width, height;
int mwidth, mheight;
} XRRScreenSize;
/*
* Events.
*/
typedef struct {
int type; /* event base */
unsigned long serial; /* # of last request processed by server */
Bool send_event; /* true if this came from a SendEvent request */
Display *display; /* Display the event was read from */
Window window; /* window which selected for this event */
Window root; /* Root window for changed screen */
Time timestamp; /* when the screen change occurred */
Time config_timestamp; /* when the last configuration change */
SizeID size_index;
SubpixelOrder subpixel_order;
Rotation rotation;
int width;
int height;
int mwidth;
int mheight;
} XRRScreenChangeNotifyEvent;
typedef struct {
int type; /* event base */
unsigned long serial; /* # of last request processed by server */
Bool send_event; /* true if this came from a SendEvent request */
Display *display; /* Display the event was read from */
Window window; /* window which selected for this event */
int subtype; /* RRNotify_ subtype */
} XRRNotifyEvent;
typedef struct {
int type; /* event base */
unsigned long serial; /* # of last request processed by server */
Bool send_event; /* true if this came from a SendEvent request */
Display *display; /* Display the event was read from */
Window window; /* window which selected for this event */
int subtype; /* RRNotify_OutputChange */
RROutput output; /* affected output */
RRCrtc crtc; /* current crtc (or None) */
RRMode mode; /* current mode (or None) */
Rotation rotation; /* current rotation of associated crtc */
Connection connection; /* current connection status */
SubpixelOrder subpixel_order;
} XRROutputChangeNotifyEvent;
typedef struct {
int type; /* event base */
unsigned long serial; /* # of last request processed by server */
Bool send_event; /* true if this came from a SendEvent request */
Display *display; /* Display the event was read from */
Window window; /* window which selected for this event */
int subtype; /* RRNotify_CrtcChange */
RRCrtc crtc; /* current crtc (or None) */
RRMode mode; /* current mode (or None) */
Rotation rotation; /* current rotation of associated crtc */
int x, y; /* position */
unsigned int width, height; /* size */
} XRRCrtcChangeNotifyEvent;
typedef struct {
int type; /* event base */
unsigned long serial; /* # of last request processed by server */
Bool send_event; /* true if this came from a SendEvent request */
Display *display; /* Display the event was read from */
Window window; /* window which selected for this event */
int subtype; /* RRNotify_OutputProperty */
RROutput output; /* related output */
Atom property; /* changed property */
Time timestamp; /* time of change */
int state; /* NewValue, Deleted */
} XRROutputPropertyNotifyEvent;
typedef struct {
int type; /* event base */
unsigned long serial; /* # of last request processed by server */
Bool send_event; /* true if this came from a SendEvent request */
Display *display; /* Display the event was read from */
Window window; /* window which selected for this event */
int subtype; /* RRNotify_ProviderChange */
RRProvider provider; /* current provider (or None) */
Time timestamp; /* time of change */
unsigned int current_role;
} XRRProviderChangeNotifyEvent;
typedef struct {
int type; /* event base */
unsigned long serial; /* # of last request processed by server */
Bool send_event; /* true if this came from a SendEvent request */
Display *display; /* Display the event was read from */
Window window; /* window which selected for this event */
int subtype; /* RRNotify_ProviderProperty */
RRProvider provider; /* related provider */
Atom property; /* changed property */
Time timestamp; /* time of change */
int state; /* NewValue, Deleted */
} XRRProviderPropertyNotifyEvent;
typedef struct {
int type; /* event base */
unsigned long serial; /* # of last request processed by server */
Bool send_event; /* true if this came from a SendEvent request */
Display *display; /* Display the event was read from */
Window window; /* window which selected for this event */
int subtype; /* RRNotify_ResourceChange */
Time timestamp; /* time of change */
} XRRResourceChangeNotifyEvent;
/* internal representation is private to the library */
typedef struct _XRRScreenConfiguration XRRScreenConfiguration;
Bool XRRQueryExtension (Display *dpy,
int *event_base_return,
int *error_base_return);
Status XRRQueryVersion (Display *dpy,
int *major_version_return,
int *minor_version_return);
XRRScreenConfiguration *XRRGetScreenInfo (Display *dpy,
Window window);
void XRRFreeScreenConfigInfo (XRRScreenConfiguration *config);
/*
* Note that screen configuration changes are only permitted if the client can
* prove it has up to date configuration information. We are trying to
* insist that it become possible for screens to change dynamically, so
* we want to ensure the client knows what it is talking about when requesting
* changes.
*/
Status XRRSetScreenConfig (Display *dpy,
XRRScreenConfiguration *config,
Drawable draw,
int size_index,
Rotation rotation,
Time timestamp);
/* added in v1.1, sorry for the lame name */
Status XRRSetScreenConfigAndRate (Display *dpy,
XRRScreenConfiguration *config,
Drawable draw,
int size_index,
Rotation rotation,
short rate,
Time timestamp);
Rotation XRRConfigRotations(XRRScreenConfiguration *config, Rotation *current_rotation);
Time XRRConfigTimes (XRRScreenConfiguration *config, Time *config_timestamp);
XRRScreenSize *XRRConfigSizes(XRRScreenConfiguration *config, int *nsizes);
short *XRRConfigRates (XRRScreenConfiguration *config, int sizeID, int *nrates);
SizeID XRRConfigCurrentConfiguration (XRRScreenConfiguration *config,
Rotation *rotation);
short XRRConfigCurrentRate (XRRScreenConfiguration *config);
int XRRRootToScreen(Display *dpy, Window root);
/*
* returns the screen configuration for the specified screen; does a lazy
* evalution to delay getting the information, and caches the result.
* These routines should be used in preference to XRRGetScreenInfo
* to avoid unneeded round trips to the X server. These are new
* in protocol version 0.1.
*/
void XRRSelectInput(Display *dpy, Window window, int mask);
/*
* the following are always safe to call, even if RandR is not implemented
* on a screen
*/
Rotation XRRRotations(Display *dpy, int screen, Rotation *current_rotation);
XRRScreenSize *XRRSizes(Display *dpy, int screen, int *nsizes);
short *XRRRates (Display *dpy, int screen, int sizeID, int *nrates);
Time XRRTimes (Display *dpy, int screen, Time *config_timestamp);
/* Version 1.2 additions */
/* despite returning a Status, this returns 1 for success */
Status
XRRGetScreenSizeRange (Display *dpy, Window window,
int *minWidth, int *minHeight,
int *maxWidth, int *maxHeight);
void
XRRSetScreenSize (Display *dpy, Window window,
int width, int height,
int mmWidth, int mmHeight);
typedef unsigned long XRRModeFlags;
typedef struct _XRRModeInfo {
RRMode id;
unsigned int width;
unsigned int height;
unsigned long dotClock;
unsigned int hSyncStart;
unsigned int hSyncEnd;
unsigned int hTotal;
unsigned int hSkew;
unsigned int vSyncStart;
unsigned int vSyncEnd;
unsigned int vTotal;
char *name;
unsigned int nameLength;
XRRModeFlags modeFlags;
} XRRModeInfo;
typedef struct _XRRScreenResources {
Time timestamp;
Time configTimestamp;
int ncrtc;
RRCrtc *crtcs;
int noutput;
RROutput *outputs;
int nmode;
XRRModeInfo *modes;
} XRRScreenResources;
XRRScreenResources *
XRRGetScreenResources (Display *dpy, Window window);
void
XRRFreeScreenResources (XRRScreenResources *resources);
typedef struct _XRROutputInfo {
Time timestamp;
RRCrtc crtc;
char *name;
int nameLen;
unsigned long mm_width;
unsigned long mm_height;
Connection connection;
SubpixelOrder subpixel_order;
int ncrtc;
RRCrtc *crtcs;
int nclone;
RROutput *clones;
int nmode;
int npreferred;
RRMode *modes;
} XRROutputInfo;
XRROutputInfo *
XRRGetOutputInfo (Display *dpy, XRRScreenResources *resources, RROutput output);
void
XRRFreeOutputInfo (XRROutputInfo *outputInfo);
Atom *
XRRListOutputProperties (Display *dpy, RROutput output, int *nprop);
typedef struct {
Bool pending;
Bool range;
Bool immutable;
int num_values;
long *values;
} XRRPropertyInfo;
XRRPropertyInfo *
XRRQueryOutputProperty (Display *dpy, RROutput output, Atom property);
void
XRRConfigureOutputProperty (Display *dpy, RROutput output, Atom property,
Bool pending, Bool range, int num_values,
long *values);
void
XRRChangeOutputProperty (Display *dpy, RROutput output,
Atom property, Atom type,
int format, int mode,
_Xconst unsigned char *data, int nelements);
void
XRRDeleteOutputProperty (Display *dpy, RROutput output, Atom property);
int
XRRGetOutputProperty (Display *dpy, RROutput output,
Atom property, long offset, long length,
Bool _delete, Bool pending, Atom req_type,
Atom *actual_type, int *actual_format,
unsigned long *nitems, unsigned long *bytes_after,
unsigned char **prop);
XRRModeInfo *
XRRAllocModeInfo (_Xconst char *name, int nameLength);
RRMode
XRRCreateMode (Display *dpy, Window window, XRRModeInfo *modeInfo);
void
XRRDestroyMode (Display *dpy, RRMode mode);
void
XRRAddOutputMode (Display *dpy, RROutput output, RRMode mode);
void
XRRDeleteOutputMode (Display *dpy, RROutput output, RRMode mode);
void
XRRFreeModeInfo (XRRModeInfo *modeInfo);
typedef struct _XRRCrtcInfo {
Time timestamp;
int x, y;
unsigned int width, height;
RRMode mode;
Rotation rotation;
int noutput;
RROutput *outputs;
Rotation rotations;
int npossible;
RROutput *possible;
} XRRCrtcInfo;
XRRCrtcInfo *
XRRGetCrtcInfo (Display *dpy, XRRScreenResources *resources, RRCrtc crtc);
void
XRRFreeCrtcInfo (XRRCrtcInfo *crtcInfo);
Status
XRRSetCrtcConfig (Display *dpy,
XRRScreenResources *resources,
RRCrtc crtc,
Time timestamp,
int x, int y,
RRMode mode,
Rotation rotation,
RROutput *outputs,
int noutputs);
int
XRRGetCrtcGammaSize (Display *dpy, RRCrtc crtc);
typedef struct _XRRCrtcGamma {
int size;
unsigned short *red;
unsigned short *green;
unsigned short *blue;
} XRRCrtcGamma;
XRRCrtcGamma *
XRRGetCrtcGamma (Display *dpy, RRCrtc crtc);
XRRCrtcGamma *
XRRAllocGamma (int size);
void
XRRSetCrtcGamma (Display *dpy, RRCrtc crtc, XRRCrtcGamma *gamma);
void
XRRFreeGamma (XRRCrtcGamma *gamma);
/* Version 1.3 additions */
XRRScreenResources *
XRRGetScreenResourcesCurrent (Display *dpy, Window window);
void
XRRSetCrtcTransform (Display *dpy,
RRCrtc crtc,
XTransform *transform,
_Xconst char *filter,
XFixed *params,
int nparams);
typedef struct _XRRCrtcTransformAttributes {
XTransform pendingTransform;
char *pendingFilter;
int pendingNparams;
XFixed *pendingParams;
XTransform currentTransform;
char *currentFilter;
int currentNparams;
XFixed *currentParams;
} XRRCrtcTransformAttributes;
/*
* Get current crtc transforms and filters.
* Pass *attributes to XFree to free
*/
Status
XRRGetCrtcTransform (Display *dpy,
RRCrtc crtc,
XRRCrtcTransformAttributes **attributes);
/*
* intended to take RRScreenChangeNotify, or
* ConfigureNotify (on the root window)
* returns 1 if it is an event type it understands, 0 if not
*/
int XRRUpdateConfiguration(XEvent *event);
typedef struct _XRRPanning {
Time timestamp;
unsigned int left;
unsigned int top;
unsigned int width;
unsigned int height;
unsigned int track_left;
unsigned int track_top;
unsigned int track_width;
unsigned int track_height;
int border_left;
int border_top;
int border_right;
int border_bottom;
} XRRPanning;
XRRPanning *
XRRGetPanning (Display *dpy, XRRScreenResources *resources, RRCrtc crtc);
void
XRRFreePanning (XRRPanning *panning);
Status
XRRSetPanning (Display *dpy,
XRRScreenResources *resources,
RRCrtc crtc,
XRRPanning *panning);
void
XRRSetOutputPrimary(Display *dpy,
Window window,
RROutput output);
RROutput
XRRGetOutputPrimary(Display *dpy,
Window window);
typedef struct _XRRProviderResources {
Time timestamp;
int nproviders;
RRProvider *providers;
} XRRProviderResources;
XRRProviderResources *
XRRGetProviderResources(Display *dpy, Window window);
void
XRRFreeProviderResources(XRRProviderResources *resources);
typedef struct _XRRProviderInfo {
unsigned int capabilities;
int ncrtcs;
RRCrtc *crtcs;
int noutputs;
RROutput *outputs;
char *name;
int nassociatedproviders;
RRProvider *associated_providers;
unsigned int *associated_capability;
int nameLen;
} XRRProviderInfo;
XRRProviderInfo *
XRRGetProviderInfo(Display *dpy, XRRScreenResources *resources, RRProvider provider);
void
XRRFreeProviderInfo(XRRProviderInfo *provider);
int
XRRSetProviderOutputSource(Display *dpy, XID provider, XID source_provider);
int
XRRSetProviderOffloadSink(Display *dpy, XID provider, XID sink_provider);
Atom *
XRRListProviderProperties (Display *dpy, RRProvider provider, int *nprop);
XRRPropertyInfo *
XRRQueryProviderProperty (Display *dpy, RRProvider provider, Atom property);
void
XRRConfigureProviderProperty (Display *dpy, RRProvider provider, Atom property,
Bool pending, Bool range, int num_values,
long *values);
void
XRRChangeProviderProperty (Display *dpy, RRProvider provider,
Atom property, Atom type,
int format, int mode,
_Xconst unsigned char *data, int nelements);
void
XRRDeleteProviderProperty (Display *dpy, RRProvider provider, Atom property);
int
XRRGetProviderProperty (Display *dpy, RRProvider provider,
Atom property, long offset, long length,
Bool _delete, Bool pending, Atom req_type,
Atom *actual_type, int *actual_format,
unsigned long *nitems, unsigned long *bytes_after,
unsigned char **prop);
typedef struct _XRRMonitorInfo {
Atom name;
Bool primary;
Bool automatic;
int noutput;
int x;
int y;
int width;
int height;
int mwidth;
int mheight;
RROutput *outputs;
} XRRMonitorInfo;
XRRMonitorInfo *
XRRAllocateMonitor(Display *dpy, int noutput);
XRRMonitorInfo *
XRRGetMonitors(Display *dpy, Window window, Bool get_active, int *nmonitors);
void
XRRSetMonitor(Display *dpy, Window window, XRRMonitorInfo *monitor);
void
XRRDeleteMonitor(Display *dpy, Window window, Atom name);
void
XRRFreeMonitors(XRRMonitorInfo *monitors);
_XFUNCPROTOEND
#endif /* _XRANDR_H_ */
#endif

200
defos/include/randr.h Normal file
View File

@ -0,0 +1,200 @@
/*
* Copyright © 2000 Compaq Computer Corporation
* Copyright © 2002 Hewlett Packard Company
* Copyright © 2006 Intel Corporation
* Copyright © 2008 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*
* Author: Jim Gettys, HP Labs, Hewlett-Packard, Inc.
* Keith Packard, Intel Corporation
*/
#if defined(DM_PLATFORM_LINUX)
#ifndef _RANDR_H_
#define _RANDR_H_
typedef unsigned short Rotation;
typedef unsigned short SizeID;
typedef unsigned short SubpixelOrder;
typedef unsigned short Connection;
typedef unsigned short XRandrRotation;
typedef unsigned short XRandrSizeID;
typedef unsigned short XRandrSubpixelOrder;
typedef unsigned long XRandrModeFlags;
#define RANDR_NAME "RANDR"
#define RANDR_MAJOR 1
#define RANDR_MINOR 5
#define RRNumberErrors 4
#define RRNumberEvents 2
#define RRNumberRequests 45
#define X_RRQueryVersion 0
/* we skip 1 to make old clients fail pretty immediately */
#define X_RROldGetScreenInfo 1
#define X_RR1_0SetScreenConfig 2
/* V1.0 apps share the same set screen config request id */
#define X_RRSetScreenConfig 2
#define X_RROldScreenChangeSelectInput 3
/* 3 used to be ScreenChangeSelectInput; deprecated */
#define X_RRSelectInput 4
#define X_RRGetScreenInfo 5
/* V1.2 additions */
#define X_RRGetScreenSizeRange 6
#define X_RRSetScreenSize 7
#define X_RRGetScreenResources 8
#define X_RRGetOutputInfo 9
#define X_RRListOutputProperties 10
#define X_RRQueryOutputProperty 11
#define X_RRConfigureOutputProperty 12
#define X_RRChangeOutputProperty 13
#define X_RRDeleteOutputProperty 14
#define X_RRGetOutputProperty 15
#define X_RRCreateMode 16
#define X_RRDestroyMode 17
#define X_RRAddOutputMode 18
#define X_RRDeleteOutputMode 19
#define X_RRGetCrtcInfo 20
#define X_RRSetCrtcConfig 21
#define X_RRGetCrtcGammaSize 22
#define X_RRGetCrtcGamma 23
#define X_RRSetCrtcGamma 24
/* V1.3 additions */
#define X_RRGetScreenResourcesCurrent 25
#define X_RRSetCrtcTransform 26
#define X_RRGetCrtcTransform 27
#define X_RRGetPanning 28
#define X_RRSetPanning 29
#define X_RRSetOutputPrimary 30
#define X_RRGetOutputPrimary 31
#define RRTransformUnit (1L << 0)
#define RRTransformScaleUp (1L << 1)
#define RRTransformScaleDown (1L << 2)
#define RRTransformProjective (1L << 3)
/* v1.4 */
#define X_RRGetProviders 32
#define X_RRGetProviderInfo 33
#define X_RRSetProviderOffloadSink 34
#define X_RRSetProviderOutputSource 35
#define X_RRListProviderProperties 36
#define X_RRQueryProviderProperty 37
#define X_RRConfigureProviderProperty 38
#define X_RRChangeProviderProperty 39
#define X_RRDeleteProviderProperty 40
#define X_RRGetProviderProperty 41
/* v1.5 */
#define X_RRGetMonitors 42
#define X_RRSetMonitor 43
#define X_RRDeleteMonitor 44
/* Event selection bits */
#define RRScreenChangeNotifyMask (1L << 0)
/* V1.2 additions */
#define RRCrtcChangeNotifyMask (1L << 1)
#define RROutputChangeNotifyMask (1L << 2)
#define RROutputPropertyNotifyMask (1L << 3)
/* V1.4 additions */
#define RRProviderChangeNotifyMask (1L << 4)
#define RRProviderPropertyNotifyMask (1L << 5)
#define RRResourceChangeNotifyMask (1L << 6)
/* Event codes */
#define RRScreenChangeNotify 0
/* V1.2 additions */
#define RRNotify 1
/* RRNotify Subcodes */
#define RRNotify_CrtcChange 0
#define RRNotify_OutputChange 1
#define RRNotify_OutputProperty 2
#define RRNotify_ProviderChange 3
#define RRNotify_ProviderProperty 4
#define RRNotify_ResourceChange 5
/* used in the rotation field; rotation and reflection in 0.1 proto. */
#define RR_Rotate_0 1
#define RR_Rotate_90 2
#define RR_Rotate_180 4
#define RR_Rotate_270 8
/* new in 1.0 protocol, to allow reflection of screen */
#define RR_Reflect_X 16
#define RR_Reflect_Y 32
#define RRSetConfigSuccess 0
#define RRSetConfigInvalidConfigTime 1
#define RRSetConfigInvalidTime 2
#define RRSetConfigFailed 3
/* new in 1.2 protocol */
#define RR_HSyncPositive 0x00000001
#define RR_HSyncNegative 0x00000002
#define RR_VSyncPositive 0x00000004
#define RR_VSyncNegative 0x00000008
#define RR_Interlace 0x00000010
#define RR_DoubleScan 0x00000020
#define RR_CSync 0x00000040
#define RR_CSyncPositive 0x00000080
#define RR_CSyncNegative 0x00000100
#define RR_HSkewPresent 0x00000200
#define RR_BCast 0x00000400
#define RR_PixelMultiplex 0x00000800
#define RR_DoubleClock 0x00001000
#define RR_ClockDivideBy2 0x00002000
#define RR_Connected 0
#define RR_Disconnected 1
#define RR_UnknownConnection 2
#define BadRROutput 0
#define BadRRCrtc 1
#define BadRRMode 2
#define BadRRProvider 3
/* Conventional RandR output properties */
#define RR_PROPERTY_BACKLIGHT "Backlight"
#define RR_PROPERTY_RANDR_EDID "EDID"
#define RR_PROPERTY_SIGNAL_FORMAT "SignalFormat"
#define RR_PROPERTY_SIGNAL_PROPERTIES "SignalProperties"
#define RR_PROPERTY_CONNECTOR_TYPE "ConnectorType"
#define RR_PROPERTY_CONNECTOR_NUMBER "ConnectorNumber"
#define RR_PROPERTY_COMPATIBILITY_LIST "CompatibilityList"
#define RR_PROPERTY_CLONE_LIST "CloneList"
#define RR_PROPERTY_BORDER "Border"
#define RR_PROPERTY_BORDER_DIMENSIONS "BorderDimensions"
#define RR_PROPERTY_GUID "GUID"
#define RR_PROPERTY_RANDR_TILE "TILE"
/* roles this device can carry out */
#define RR_Capability_None 0
#define RR_Capability_SourceOutput 1
#define RR_Capability_SinkOutput 2
#define RR_Capability_SourceOffload 4
#define RR_Capability_SinkOffload 8
#endif /* _RANDR_H_ */
#endif

Binary file not shown.

View File

@ -144,6 +144,32 @@ static int is_maximized(lua_State *L)
return 1;
}
static int toggle_always_on_top(lua_State *L)
{
defos_toggle_always_on_top();
return 0;
}
static int set_always_on_top(lua_State *L)
{
if (checkboolean(L, 1) != defos_is_always_on_top()) {
defos_toggle_always_on_top();
}
return 0;
}
static int is_always_on_top(lua_State *L)
{
lua_pushboolean(L, defos_is_always_on_top());
return 1;
}
static int minimize(lua_State *L)
{
defos_minimize();
return 0;
}
static int set_window_icon(lua_State *L)
{
const char *icon_path = luaL_checkstring(L, 1);
@ -159,19 +185,18 @@ static int get_bundle_root(lua_State *L)
return 1;
}
static int get_parameters(lua_State *L)
static int get_arguments(lua_State *L)
{
dmArray<char*>* parameters = new dmArray<char*>();
defos_get_parameters(parameters);
dmArray<char*> arguments;
defos_get_arguments(arguments);
lua_newtable(L);
for(int i = 0; i < parameters->Size(); i++)
for (unsigned int i = 0; i < arguments.Size(); i++)
{
char* param = (*parameters)[i];
lua_pushstring(L, param);
char* arg = arguments[i];
lua_pushstring(L, arg);
lua_rawseti(L, 1, i+1);
free(param);
free(arg);
}
delete parameters;
return 1;
}
@ -209,6 +234,24 @@ static int is_cursor_visible(lua_State *L)
return 1;
}
static int get_cursor_pos(lua_State *L)
{
WinPoint point;
point = defos_get_cursor_pos();
lua_pushnumber(L, point.x);
lua_pushnumber(L, point.y);
return 2;
}
static int get_cursor_pos_view(lua_State *L)
{
WinPoint point;
point = defos_get_cursor_pos_view();
lua_pushnumber(L, point.x);
lua_pushnumber(L, point.y);
return 2;
}
static int set_cursor_pos(lua_State *L)
{
float x = luaL_checknumber(L, 1);
@ -216,11 +259,11 @@ static int set_cursor_pos(lua_State *L)
defos_set_cursor_pos(x, y);
return 0;
}
static int move_cursor_to(lua_State *L)
static int set_cursor_pos_view(lua_State *L)
{
float x = luaL_checknumber(L, 1);
float y = luaL_checknumber(L, 2);
defos_move_cursor_to(x, y);
defos_set_cursor_pos_view(x, y);
return 0;
}
@ -319,6 +362,118 @@ static int reset_cursor(lua_State *L)
return 0;
}
// Displays
static void push_display_mode(lua_State *L, const DisplayModeInfo &mode)
{
lua_newtable(L);
lua_pushnumber(L, mode.width);
lua_setfield(L, -2, "width");
lua_pushnumber(L, mode.height);
lua_setfield(L, -2, "height");
lua_pushnumber(L, mode.refresh_rate);
lua_setfield(L, -2, "refresh_rate");
lua_pushnumber(L, mode.scaling_factor);
lua_setfield(L, -2, "scaling_factor");
lua_pushnumber(L, mode.bits_per_pixel);
lua_setfield(L, -2, "bits_per_pixel");
lua_pushnumber(L, mode.orientation);
lua_setfield(L, -2, "orientation");
lua_pushboolean(L, mode.reflect_x);
lua_setfield(L, -2, "reflect_x");
lua_pushboolean(L, mode.reflect_y);
lua_setfield(L, -2, "reflect_y");
}
static int get_displays(lua_State *L)
{
dmArray<DisplayInfo> displayList;
defos_get_displays(displayList);
lua_newtable(L); // Final result
for (int i = 0; i < displayList.Size(); i++)
{
DisplayInfo &display = displayList[i];
lua_newtable(L); // The display info table
#ifdef DM_PLATFORM_WINDOWS
lua_pushstring(L, display.id);
free(const_cast<char*>(display.id));
#else
lua_pushlightuserdata(L, display.id);
#endif
lua_pushvalue(L, -1);
lua_setfield(L, -3, "id");
// screen positioning bounds
lua_newtable(L);
lua_pushnumber(L, display.bounds.x);
lua_setfield(L, -2, "x");
lua_pushnumber(L, display.bounds.y);
lua_setfield(L, -2, "y");
lua_pushnumber(L, display.bounds.w);
lua_setfield(L, -2, "width");
lua_pushnumber(L, display.bounds.h);
lua_setfield(L, -2, "height");
lua_setfield(L, -3, "bounds");
push_display_mode(L, display.mode);
lua_setfield(L, -3, "mode");
if (display.name)
{
lua_pushstring(L, display.name);
lua_setfield(L, -3, "name");
free(display.name);
}
// result[id] = display
lua_pushvalue(L, -2);
lua_settable(L, -4);
// result[i + 1] = display
lua_rawseti(L, -2, i + 1);
}
return 1;
}
static int get_display_modes(lua_State *L)
{
#ifdef DM_PLATFORM_WINDOWS
DisplayID displayID = luaL_checkstring(L, 1);
#else
luaL_checktype(L, 1, LUA_TLIGHTUSERDATA);
DisplayID displayID = lua_touserdata(L, 1);
#endif
dmArray<DisplayModeInfo> modeList;
defos_get_display_modes(displayID, modeList);
lua_newtable(L);
for (int i = 0; i < modeList.Size(); i++)
{
push_display_mode(L, modeList[i]);
lua_rawseti(L, -2, i + 1);
}
return 1;
}
static int get_current_display_id(lua_State *L)
{
DisplayID displayID = defos_get_current_display();
#ifdef DM_PLATFORM_WINDOWS
lua_pushstring(L, displayID);
free(const_cast<char*>(displayID));
#else
lua_pushlightuserdata(L, displayID);
#endif
return 1;
}
// Events
static void set_event_handler(lua_State *L, int index, DefosEvent event)
@ -366,7 +521,7 @@ static int on_mouse_enter(lua_State *L)
static int on_click(lua_State *L)
{
#ifndef DM_PLATFORM_HTML5
dmLogInfo("Event 'on_click' exists only on HTML5");
dmLogWarning("Event 'on_click' exists only in HTML5");
#endif
set_event_handler(L, 1, DEFOS_EVENT_CLICK);
return 0;
@ -419,10 +574,14 @@ static const luaL_reg Module_methods[] =
{"toggle_fullscreen", toggle_fullscreen},
{"set_fullscreen", set_fullscreen},
{"is_fullscreen", is_fullscreen},
{"toggle_always_on_top", toggle_always_on_top},
{"set_always_on_top", set_always_on_top},
{"is_always_on_top", is_always_on_top},
{"toggle_maximize", toggle_maximized}, // For backwards compatibility
{"toggle_maximized", toggle_maximized},
{"set_maximized", set_maximized},
{"is_maximized", is_maximized},
{"minimize", minimize},
{"set_console_visible", set_console_visible},
{"is_console_visible", is_console_visible},
{"set_cursor_visible", set_cursor_visible},
@ -431,8 +590,11 @@ static const luaL_reg Module_methods[] =
{"on_mouse_leave", on_mouse_leave},
{"on_click", on_click},
{"is_mouse_in_view", is_mouse_in_view},
{"get_cursor_pos", get_cursor_pos},
{"get_cursor_pos_view", get_cursor_pos_view},
{"set_cursor_pos", set_cursor_pos},
{"move_cursor_to", move_cursor_to},
{"set_cursor_pos_view", set_cursor_pos_view},
{"move_cursor_to", set_cursor_pos_view}, // For backwards compatibility
{"set_cursor_clipped", set_cursor_clipped},
{"is_cursor_clipped", is_cursor_clipped},
{"set_cursor_locked", set_cursor_locked},
@ -442,16 +604,20 @@ static const luaL_reg Module_methods[] =
{"get_view_size", get_view_size},
{"set_cursor", set_cursor},
{"reset_cursor", reset_cursor},
{"get_displays", get_displays},
{"get_display_modes", get_display_modes},
{"get_current_display_id", get_current_display_id},
{"set_window_icon", set_window_icon},
{"get_bundle_root", get_bundle_root},
{"get_parameters", get_parameters},
{"get_arguments", get_arguments},
{"get_parameters", get_arguments}, // For backwards compatibility
{0, 0}};
static void LuaInit(lua_State *L)
{
int top = lua_gettop(L);
luaL_register(L, MODULE_NAME, Module_methods);
lua_pushnumber(L, DEFOS_CURSOR_ARROW);
lua_setfield(L, -2, "CURSOR_ARROW");
lua_pushnumber(L, DEFOS_CURSOR_CROSSHAIR);
@ -460,15 +626,15 @@ static void LuaInit(lua_State *L)
lua_setfield(L, -2, "CURSOR_HAND");
lua_pushnumber(L, DEFOS_CURSOR_IBEAM);
lua_setfield(L, -2, "CURSOR_IBEAM");
#if defined(DM_PLATFORM_WINDOWS)
lua_pushstring(L, "\\");
lua_setfield(L, -2, "PATH_SEP");
#else
lua_pushstring(L, "/");
lua_setfield(L, -2, "PATH_SEP");
#endif
#endif
lua_pop(L, 1);
assert(top == lua_gettop(L));
}
@ -510,4 +676,4 @@ dmExtension::Result FinalizeDefos(dmExtension::Params *params)
}
DM_DECLARE_EXTENSION(EXTENSION_NAME, LIB_NAME, 0, 0, InitializeDefos, 0, 0, FinalizeDefos)
#endif
#endif

View File

@ -49,9 +49,17 @@ void defos_init() {
Module.__defosjs_click_listener = function () {
_defos_emit_event_from_js($2);
};
Module.__defosjs_mousemove_listener = function (evt) {
var rect = Module.canvas.getBoundingClientRect();
Module.__defosjs_mouse_x = evt.clientX - rect.left;
Module.__defosjs_mouse_y = evt.clientY - rect.top;
};
Module.__defosjs_mouse_x = -1;
Module.__defosjs_mouse_y = -1;
Module.canvas.addEventListener('mouseenter', Module.__defosjs_mouseenter_listener);
Module.canvas.addEventListener('mouseleave', Module.__defosjs_mouseleave_listener);
Module.canvas.addEventListener('click', Module.__defosjs_click_listener);
document.addEventListener('mousemove', Module.__defosjs_mousemove_listener);
}, DEFOS_EVENT_MOUSE_ENTER, DEFOS_EVENT_MOUSE_LEAVE, DEFOS_EVENT_CLICK);
EM_ASM_({
@ -86,6 +94,7 @@ void defos_final() {
Module.canvas.removeEventListener('mouseenter', Module.__defosjs_mouseenter_listener);
Module.canvas.removeEventListener('mouseleave', Module.__defosjs_mouseleave_listener);
Module.canvas.removeEventListener('click', Module.__defosjs_click_listener);
document.removeEventListener('mousemove', Module.__defosjs_mousemove_listener);
document.removeEventListener('pointerlockchange', Module.__defosjs_pointerlockchange_listener);
document.removeEventListener('mozpointerlockchange', Module.__defosjs_pointerlockchange_listener);
document.removeEventListener('webkitpointerlockchange', Module.__defosjs_pointerlockchange_listener);
@ -100,15 +109,19 @@ void defos_event_handler_was_set(DefosEvent event) {
}
void defos_disable_maximize_button() {
dmLogInfo("Method 'disable_maximize_button' is not supported in html5");
dmLogWarning("Method 'disable_maximize_button' is not supported in HTML5");
}
void defos_disable_minimize_button() {
dmLogInfo("Method 'disable_minimize_button' is not supported in html5");
dmLogWarning("Method 'disable_minimize_button' is not supported in HTML5");
}
void defos_disable_window_resize() {
dmLogInfo("Method 'disable_window_resize' is not supported in html5");
dmLogWarning("Method 'disable_window_resize' is not supported in HTML5");
}
void defos_minimize() {
dmLogWarning("Method 'minimize' is not supported in HTML5");
}
void defos_toggle_fullscreen() {
@ -140,6 +153,14 @@ bool defos_is_maximized() {
return is_maximized;
}
void defos_toggle_always_on_top() {
dmLogWarning("Method 'toggle_always_on_top' is not supported in HTML5");
}
bool defos_is_always_on_top() {
return false;
}
void defos_set_window_title(const char* title_lua) {
EM_ASM_({document.title = UTF8ToString($0)}, title_lua);
}
@ -152,7 +173,7 @@ void defos_set_window_icon(const char *icon_path)
if (oldLink) { document.head.removeChild(oldLink); }
var link = document.createElement('link');
link.rel = 'shortcut icon';
link.href = src;
link.href = src;
document.head.appendChild(link);
}
changeFavicon(UTF8ToString($0));
@ -170,16 +191,16 @@ char* defos_get_bundle_root() {
return bundlePath;
}
void defos_get_parameters(dmArray<char*>* parameters) {
char*param = (char*)EM_ASM_INT({
void defos_get_arguments(dmArray<char*> &arguments) {
char *param = (char*)EM_ASM_INT({
var jsString = window.location.search;
var lengthBytes = lengthBytesUTF8(jsString) + 1;
var stringOnWasmHeap = _malloc(lengthBytes);
stringToUTF8(jsString, stringOnWasmHeap, lengthBytes+1);
return stringOnWasmHeap;
},0);
parameters->OffsetCapacity(1);
parameters->Push(param);
arguments.OffsetCapacity(1);
arguments.Push(param);
}
void defos_set_window_size(float x, float y, float w, float h) {
@ -215,7 +236,7 @@ bool defos_is_console_visible() {
}
void defos_set_console_visible(bool visible) {
dmLogInfo("Method 'defos_set_console_visible' is not supported in html5, it is meant for Windows builds only");
dmLogWarning("Method 'set_console_visible' is only supported on Windows");
}
void defos_set_cursor_visible(bool visible) {
@ -236,16 +257,27 @@ bool defos_is_mouse_in_view() {
return is_mouse_inside;
}
void defos_set_cursor_pos(float x, float y) {
dmLogInfo("Method 'defos_set_cursor_pos' is not supported in html5");
WinPoint defos_get_cursor_pos() {
return defos_get_cursor_pos_view();
}
void defos_move_cursor_to(float x, float y) {
dmLogInfo("Method 'defos_move_cursor_to' is not supported in html5");
WinPoint defos_get_cursor_pos_view() {
WinPoint point;
point.x = (float)EM_ASM_DOUBLE({ return Module.__defosjs_mouse_x }, 0.0);
point.y = (float)EM_ASM_DOUBLE({ return Module.__defosjs_mouse_y }, 0.0);
return point;
}
void defos_set_cursor_pos(float x, float y) {
dmLogWarning("Method 'defos_set_cursor_pos' is not supported in HTML5");
}
void defos_set_cursor_pos_view(float x, float y) {
dmLogWarning("Method 'defos_set_cursor_pos_view' is not supported in HTML5");
}
void defos_set_cursor_clipped(bool clipped) {
dmLogInfo("Method 'defos_set_cursor_clipped' is not supported in html5");
dmLogWarning("Method 'defos_set_cursor_clipped' is not supported in HTML5");
}
bool defos_is_cursor_clipped() {
@ -334,4 +366,14 @@ void defos_reset_cursor() {
}
}
void defos_get_displays(dmArray<DisplayInfo> &displayList) {
}
void defos_get_display_modes(DisplayID displayID, dmArray<DisplayModeInfo> &modeList) {
}
DisplayID defos_get_current_display() {
return NULL;
}
#endif

View File

@ -10,6 +10,13 @@
3. https://github.com/yetanothergeek/xctrl/blob/master/src/xctrl.c
*/
/* TODO:
ON_MOUSE_ENTER / ON_MOUSE_LEAVE
cursor locking
cursor clipping
setting the window icon
*/
#include "defos_private.h"
#include <X11/Xlib.h>
#include <X11/Xatom.h>
@ -17,10 +24,17 @@
#include <X11/Xos.h>
#include <X11/cursorfont.h>
#include <Xcursor.h>
#include <Xrandr.h>
#include <stdlib.h>
#include <unistd.h>
#include <libgen.h>
#include <sys/auxv.h>
#include <limits.h>
//static GC gc;
#define _NET_WM_STATE_REMOVE 0
#define _NET_WM_STATE_ADD 1
#define _NET_WM_STATE_REMOVE 0
#define _NET_WM_STATE_ADD 1
#define _NET_WM_STATE_TOGGLE 2
#define XATOM(name) XInternAtom(disp, name, False)
@ -36,18 +50,21 @@ static Atom NET_WM_STATE;
static Atom NET_WM_STATE_FULLSCREEN;
static Atom NET_WM_STATE_MAXIMIZED_VERT;
static Atom NET_WM_STATE_MAXIMIZED_HORZ;
static Atom NET_WM_STATE_ABOVE;
static Atom NET_WM_ALLOWED_ACTIONS;
static Atom NET_WM_ACTION_MAXIMIZE_HORZ;
static Atom NET_WM_ACTION_MAXIMIZE_VERT;
static Atom NET_WM_ACTION_MINIMIZE;
static Atom NET_FRAME_EXTENTS;
// TODO: should query state from system
static bool is_maximized = false;
static bool is_fullscreen = false;
static Cursor custom_cursor;// image cursor
static Cursor custom_cursor; // image cursor
static bool has_custom_cursor = false;
static Cursor invisible_cursor;
static bool is_cursor_visible = true;
static bool resize_locked = false;
static bool is_window_visible(Window window);
static void send_message(Window& window, Atom type, long a, long b, long c, long d,long e);
static void send_message(Window &window, Atom type, long a, long b, long c, long d, long e);
void defos_init()
{
@ -63,120 +80,242 @@ void defos_init()
NET_WM_STATE_FULLSCREEN = XATOM("_NET_WM_STATE_FULLSCREEN");
NET_WM_STATE_MAXIMIZED_VERT = XATOM("_NET_WM_STATE_MAXIMIZED_VERT");
NET_WM_STATE_MAXIMIZED_HORZ = XATOM("_NET_WM_STATE_MAXIMIZED_HORZ");
NET_WM_STATE_ABOVE = XATOM("_NET_WM_STATE_ABOVE");
NET_WM_ALLOWED_ACTIONS = XATOM("_NET_WM_ALLOWED_ACTIONS");
NET_WM_ACTION_MINIMIZE = XATOM("_NET_WM_ACTION_MINIMIZE");
NET_WM_ACTION_MAXIMIZE_HORZ = XATOM("_NET_WM_ACTION_MAXIMIZE_HORZ");
NET_WM_ACTION_MAXIMIZE_VERT = XATOM("_NET_WM_ACTION_MAXIMIZE_VERT");
NET_FRAME_EXTENTS = XATOM("_NET_FRAME_EXTENTS");
resize_locked = false;
// Create invisible cursor
Pixmap bitmapNoData;
XColor black;
static char noData[] = { 0,0,0,0,0,0,0,0 };
black.red = black.green = black.blue = 0;
bitmapNoData = XCreateBitmapFromData(disp, win, noData, 8, 8);
invisible_cursor = XCreatePixmapCursor(disp, bitmapNoData, bitmapNoData, &black, &black, 0, 0);
XFreePixmap(disp, bitmapNoData);
is_cursor_visible = true;
}
void defos_final()
{
if(custom_cursor==NULL)
if (has_custom_cursor)
{
XFreeCursor(disp, custom_cursor);
has_custom_cursor = false;
}
XFreeCursor(disp, invisible_cursor);
}
void defos_event_handler_was_set(DefosEvent event)
{
}
static Atom* get_atom_list(Atom property, unsigned long &nItems)
{
Atom actualType;
int actualFormat;
unsigned long bytesAfter;
Atom* data = NULL;
XEvent event;
while (XGetWindowProperty(disp, win, property,
0, (~0L), False, AnyPropertyType,
&actualType, &actualFormat,
&nItems, &bytesAfter, (unsigned char**)&data) == Success && bytesAfter != 0
) {
XNextEvent(disp, &event);
}
return data;
}
static bool hint_state_contains_atom(Atom atom)
{
unsigned long nItems;
Atom* data = get_atom_list(NET_WM_STATE, nItems);
if (data) {
for (unsigned int i = 0; i < nItems; i++) {
if (data[i] == atom) {
XFree(data);
return true;
}
}
XFree(data);
}
return false;
}
bool defos_is_fullscreen()
{
return is_fullscreen;
return hint_state_contains_atom(NET_WM_STATE_FULLSCREEN);
}
bool defos_is_maximized()
{
return is_maximized;
return hint_state_contains_atom(NET_WM_STATE_MAXIMIZED_VERT);
}
bool defos_is_always_on_top()
{
return hint_state_contains_atom(NET_WM_STATE_ABOVE);
}
bool defos_is_mouse_in_view()
{
return false;
Window d1, d2;
int x, y, d3, d4;
unsigned int d5;
if (!XQueryPointer(disp, win, &d1, &d2, &d3, &d4, &x, &y, &d5)) { return false; }
if (x < 0 || y < 0) { return false; }
unsigned int w, h, d6;
XGetGeometry(disp, win, &d1, &d3, &d4, &w, &h, &d5, &d6);
if ((unsigned)x >= w || (unsigned)y >= h) { return false; }
return true;
}
void defos_disable_maximize_button()
{
dmLogInfo("Method 'defos_disable_maximize_button' is not supported in Linux");
unsigned long nItems;
Atom* data = get_atom_list(NET_WM_ALLOWED_ACTIONS, nItems);
if (!data) { return; }
// Filter the allowed actions list
Atom* newList = (Atom*)malloc(sizeof(Atom) * nItems);
unsigned long newNItems = 0;
for (unsigned long i = 0; i < nItems; i++) {
if (data[i] == NET_WM_ACTION_MAXIMIZE_HORZ || data[i] == NET_WM_ACTION_MAXIMIZE_VERT) { continue; }
newList[newNItems++] = data[i];
}
XFree(data);
XChangeProperty(disp, win, NET_WM_ALLOWED_ACTIONS, XA_ATOM, 32, PropModeReplace, (unsigned char*)newList, newNItems);
XFlush(disp);
free(newList);
}
void defos_disable_minimize_button()
{
dmLogInfo("Method 'defos_disable_minimize_button' is not supported in Linux");
unsigned long nItems;
Atom* data = get_atom_list(NET_WM_ALLOWED_ACTIONS, nItems);
if (!data) { return; }
// Filter the allowed actions list
Atom* newList = (Atom*)malloc(sizeof(Atom) * nItems);
unsigned long newNItems = 0;
for (unsigned long i = 0; i < nItems; i++) {
if (data[i] == NET_WM_ACTION_MINIMIZE) { return; }
newList[newNItems++] = data[i];
}
XFree(data);
XChangeProperty(disp, win, NET_WM_ALLOWED_ACTIONS, XA_ATOM, 32, PropModeReplace, (unsigned char*)newList, newNItems);
XFlush(disp);
free(newList);
}
static void lock_resize(int width, int height)
{
XSizeHints *sizeHints = XAllocSizeHints();
sizeHints->flags = PMinSize | PMaxSize;
sizeHints->min_width = width;
sizeHints->min_height = height;
sizeHints->max_width = width;
sizeHints->max_height = height;
XSetWMNormalHints(disp, win, sizeHints);
XFlush(disp);
XFree(sizeHints);
resize_locked = true;
}
void defos_disable_window_resize()
{
dmLogInfo("Method 'defos_disable_window_resize' is not supported in Linux");
int x, y;
unsigned int w, h, bw, depth;
Window dummy;
XGetGeometry(disp, win, &dummy, &x, &y, &w, &h, &bw, &depth);
lock_resize(w, h);
}
void defos_set_cursor_visible(bool visible)
{
dmLogInfo("Method 'defos_set_cursor_visible' is not supported in Linux");
if (visible == is_cursor_visible) { return; }
is_cursor_visible = visible;
if (visible)
{
XDefineCursor(disp, win, has_custom_cursor ? custom_cursor : None);
} else {
XDefineCursor(disp, win, invisible_cursor);
}
}
bool defos_is_cursor_visible()
{
return false;
return is_cursor_visible;
}
void defos_toggle_fullscreen()
{
if(!is_fullscreen)
{
send_message(win,
NET_WM_STATE,
_NET_WM_STATE_ADD,
NET_WM_STATE_FULLSCREEN,
0,
1,
0);
;
}
else
{
send_message(win,
NET_WM_STATE,
_NET_WM_STATE_REMOVE,
NET_WM_STATE_FULLSCREEN,
0,
1,
0);
}
is_fullscreen = !is_fullscreen;
send_message(win,
NET_WM_STATE,
_NET_WM_STATE_TOGGLE,
NET_WM_STATE_FULLSCREEN,
0,
1,
0
);
XFlush(disp);
}
void defos_toggle_maximized()
{
if(!is_maximized)
{
send_message(win,
NET_WM_STATE,
_NET_WM_STATE_ADD,
NET_WM_STATE_MAXIMIZED_VERT,
NET_WM_STATE_MAXIMIZED_HORZ,
1,
0);
}
else
{
send_message(win,
NET_WM_STATE,
_NET_WM_STATE_REMOVE,
NET_WM_STATE_MAXIMIZED_VERT,
NET_WM_STATE_MAXIMIZED_HORZ,
1,
0);
}
is_maximized = !is_maximized;
send_message(win,
NET_WM_STATE,
_NET_WM_STATE_TOGGLE,
NET_WM_STATE_MAXIMIZED_VERT,
NET_WM_STATE_MAXIMIZED_HORZ,
1,
0
);
XFlush(disp);
}
void defos_toggle_always_on_top()
{
send_message(win,
NET_WM_STATE,
defos_is_always_on_top() ? _NET_WM_STATE_REMOVE : _NET_WM_STATE_ADD,
NET_WM_STATE_ABOVE,
0,
1,
0
);
XFlush(disp);
}
void defos_minimize()
{
XIconifyWindow(disp, win, screen);
}
void defos_set_console_visible(bool visible)
{
dmLogInfo("Method 'defos_set_console_visible' is not supported in Linux");
dmLogWarning("Method 'set_console_visible' is only supported on Windows");
}
bool defos_is_console_visible()
@ -184,19 +323,58 @@ bool defos_is_console_visible()
return false;
}
typedef struct {
long left, right, top, bottom;
} WindowExtents;
static WindowExtents get_window_extents()
{
Atom actualType;
int actualFormat;
unsigned long nitems, bytesAfter;
long* extents = NULL;
XEvent event;
while (XGetWindowProperty(disp, win, NET_FRAME_EXTENTS,
0, 4, False, AnyPropertyType,
&actualType, &actualFormat,
&nitems, &bytesAfter, (unsigned char**)&extents) == Success && bytesAfter != 0
) {
XNextEvent(disp, &event);
}
if (!extents || nitems != 4) {
WindowExtents result = { 0, 0, 0, 0 };
if (extents) { XFree(extents); }
return result;
}
WindowExtents result = { extents[0], extents[1], extents[2], extents[3] };
if (extents) { XFree(extents); }
return result;
}
static RRCrtc get_current_crtc(WinRect &bounds);
void defos_set_window_size(float x, float y, float w, float h)
{
// change size only if it is visible
if(is_window_visible(win))
if (is_window_visible(win))
{
if(isnan(x) || isnan(y)){
XWindowAttributes attributes;
XGetWindowAttributes(disp, root, &attributes);
x = ((float)attributes.width - w)/2;
y = ((float)attributes.height - h)/2;
if (isnan(x) || isnan(y))
{
WinRect screenBounds;
get_current_crtc(screenBounds);
if (isnan(x)) { x = screenBounds.x + ((float)screenBounds.w - w) / 2; }
if (isnan(y)) { y = screenBounds.y + ((float)screenBounds.h - h) / 2; }
}
WindowExtents extents = get_window_extents();
w -= extents.left + extents.right;
h -= extents.top + extents.bottom;
x += extents.left;
y += extents.top;
if (resize_locked) { lock_resize(w, h); }
XMoveResizeWindow(disp, win, (int)x, (int)y, (unsigned int)w, (unsigned int)h);
XFlush(disp);
}
@ -204,68 +382,96 @@ void defos_set_window_size(float x, float y, float w, float h)
void defos_set_view_size(float x, float y, float w, float h)
{
XWindowChanges changes;
changes.x = (int)x;
changes.y = (int)y;
changes.width = (int)w;
changes.height = (int)h;
XConfigureWindow(disp, win, CWX | CWY | CWWidth | CWHeight, &changes);
XFlush(disp);
// change size only if it is visible
if (is_window_visible(win))
{
if (isnan(x) || isnan(y))
{
WinRect screenBounds;
get_current_crtc(screenBounds);
if (isnan(x)) { x = screenBounds.x + ((float)screenBounds.w - w) / 2; }
if (isnan(y)) { y = screenBounds.y + ((float)screenBounds.h - h) / 2; }
}
if (resize_locked) { lock_resize(w, h); }
XMoveResizeWindow(disp, win, (int)x, (int)y, (unsigned int)w, (unsigned int)h);
XFlush(disp);
}
}
void defos_set_window_title(const char *title_lua)
{
XChangeProperty(disp, win, NET_WM_NAME, UTF8_STRING, 8, PropModeReplace, (unsigned char*)title_lua, strlen(title_lua));
XChangeProperty(disp, win, NET_WM_NAME, UTF8_STRING, 8, PropModeReplace, (unsigned char *)title_lua, strlen(title_lua));
XFlush(disp); // IMPORTANT: we have to flush, or nothing will be changed
}
WinRect defos_get_window_size()
{
WinRect r = {0.0f, 0.0f, 0.0f, 0.0f};
return r;
WindowExtents extents = get_window_extents();
WinRect size = defos_get_view_size();
size.w += extents.left + extents.right;
size.h += extents.top + extents.bottom;
size.x -= extents.left;
size.y -= extents.top;
return size;
}
WinRect defos_get_view_size()
{
int x,y;
int x, y;
unsigned int w, h, bw, depth;
Window dummy;
XGetGeometry(disp, win, &dummy, &x, &y, &w, &h, &bw, &depth);
XTranslateCoordinates(disp, win, root, x, y, &x, &y, &dummy);
XTranslateCoordinates(disp, win, root, 0, 0, &x, &y, &dummy);
WinRect r = {(float)x, (float)y, (float)w, (float)h};
return r;
}
WinPoint defos_get_cursor_pos()
{
WinPoint point = { .x = -INFINITY, .y = -INFINITY };
Window d1, d2;
int x, y, d3, d4;
unsigned int d5;
if (XQueryPointer(disp, root, &d1, &d2, &d3, &d4, &x, &y, &d5)) {
point.x = x;
point.y = y;
}
return point;
}
WinPoint defos_get_cursor_pos_view()
{
WinPoint point = { .x = -INFINITY, .y = -INFINITY };
Window d1, d2;
int x, y, d3, d4;
unsigned int d5;
if (XQueryPointer(disp, win, &d1, &d2, &d3, &d4, &x, &y, &d5)) {
point.x = x;
point.y = y;
}
return point;
}
void defos_set_cursor_pos(float x, float y)
{
XWarpPointer(disp, None, root, 0, 0, 0, 0, (int)x, (int)y);
XFlush(disp);
}
void defos_move_cursor_to(float x, float y)
void defos_set_cursor_pos_view(float x, float y)
{
WinRect rect = defos_get_window_size();
int ix = (int)x;
int iy = (int)y;
// TODO: need this?
if(ix > rect.w) ix = rect.w;
if(ix < 0) ix = 0;
if(iy > rect.h) iy=rect.h;
if(iy < 0) iy = 0;
XWarpPointer(disp, None, win, 0, 0, 0, 0, ix, iy);
XFlush(disp);
XWarpPointer(disp, None, win, 0, 0, 0, 0, (int)x, (int)y);
XFlush(disp);
}
void defos_set_cursor_clipped(bool clipped)
{
dmLogInfo("Method 'defos_set_cursor_clipped' is not supported in Linux");
dmLogWarning("Method 'set_cursor_clipped' is not supported on Linux");
}
bool defos_is_cursor_clipped()
@ -275,7 +481,7 @@ bool defos_is_cursor_clipped()
void defos_set_cursor_locked(bool locked)
{
dmLogInfo("Method 'defos_set_cursor_locked' is not supported in Linux");
dmLogWarning("Method 'set_cursor_locked' is not supported on Linux");
}
bool defos_is_cursor_locked()
@ -283,51 +489,54 @@ bool defos_is_cursor_locked()
return false;
}
void defos_update() {
void defos_update()
{
}
void defos_set_custom_cursor_linux(const char *filename)
{
custom_cursor = XcursorFilenameLoadCursor(disp, filename);
XDefineCursor(disp, win, custom_cursor);
Cursor cursor = XcursorFilenameLoadCursor(disp, filename);
if (is_cursor_visible) { XDefineCursor(disp, win, cursor); }
if (has_custom_cursor) { XFreeCursor(disp, custom_cursor); }
custom_cursor = cursor;
has_custom_cursor = true;
}
static unsigned int get_cursor(DefosCursor cursor);
void defos_set_cursor(DefosCursor cursor)
void defos_set_cursor(DefosCursor cursor_type)
{
// TODO: X11 support change the cursor color, add it later
defos_reset_cursor();
custom_cursor = XCreateFontCursor(disp, get_cursor(cursor));
XDefineCursor(disp, win, custom_cursor);
Cursor cursor = XCreateFontCursor(disp, get_cursor(cursor_type));
if (is_cursor_visible) { XDefineCursor(disp, win, cursor); }
if (has_custom_cursor) { XFreeCursor(disp, custom_cursor); }
custom_cursor = cursor;
has_custom_cursor = true;
}
void defos_reset_cursor()
{
XUndefineCursor(disp, win);
if(custom_cursor!=NULL)
if (has_custom_cursor)
{
if (is_cursor_visible) { XUndefineCursor(disp, win); }
XFreeCursor(disp, custom_cursor);
custom_cursor = NULL;
has_custom_cursor = false;
}
}
static unsigned int get_cursor(DefosCursor cursor)
{
switch(cursor)
switch (cursor)
{
case DEFOS_CURSOR_ARROW:
return XC_left_ptr;
case DEFOS_CURSOR_CROSSHAIR:
return XC_tcross;
case DEFOS_CURSOR_HAND:
return XC_hand2;
case DEFOS_CURSOR_IBEAM:
return XC_xterm;
default:
return XC_left_ptr;
case DEFOS_CURSOR_ARROW:
return XC_left_ptr;
case DEFOS_CURSOR_CROSSHAIR:
return XC_tcross;
case DEFOS_CURSOR_HAND:
return XC_hand2;
case DEFOS_CURSOR_IBEAM:
return XC_xterm;
default:
return XC_left_ptr;
}
}
@ -338,8 +547,281 @@ static bool is_window_visible(Window window)
return attributes.map_state == IsViewable;
}
static const XRRModeInfo* get_mode_info(const XRRScreenResources* screenResources, RRMode id){
for (int i = 0; i < screenResources->nmode; i++)
{
if (screenResources->modes[i].id == id)
{
return screenResources->modes + i;
}
}
return NULL;
}
static double compute_refresh_rate(const XRRModeInfo* modeInfo)
{
if (!modeInfo->hTotal || !modeInfo->vTotal)
{
return 0;
}
return ((double)modeInfo->dotClock / ((double)modeInfo->hTotal * (double)modeInfo->vTotal));
}
static bool axis_flipped(Rotation rotation)
{
return !!(rotation & (RR_Rotate_90 | RR_Rotate_270));
}
static unsigned long orientation_from_rotation(Rotation rotation)
{
if (rotation & RR_Rotate_0) { return 0; }
if (rotation & RR_Rotate_90) { return 90; }
if (rotation & RR_Rotate_180) { return 180; }
if (rotation & RR_Rotate_270) { return 270; }
return 0;
}
static void parse_display_mode(const XRRModeInfo* modeInfo, DisplayModeInfo &mode, Rotation rotation)
{
if (axis_flipped(rotation))
{
mode.width = modeInfo->height;
mode.height = modeInfo->width;
} else {
mode.width = modeInfo->width;
mode.height = modeInfo->height;
}
mode.refresh_rate = compute_refresh_rate(modeInfo);
mode.bits_per_pixel = 32;
mode.scaling_factor = 1.0;
mode.orientation = orientation_from_rotation(rotation);
mode.reflect_x = !!(rotation & RR_Reflect_X);
mode.reflect_y = !!(rotation & RR_Reflect_Y);
}
void defos_get_displays(dmArray<DisplayInfo> &displayList)
{
XRRScreenResources *screenResources = XRRGetScreenResourcesCurrent(disp, win);
unsigned long bpp = (long)DefaultDepth(disp, screen);
displayList.OffsetCapacity(screenResources->ncrtc);
for (int i = 0; i < screenResources->ncrtc; i++)
{
RRCrtc crtc = screenResources->crtcs[i];
DisplayInfo display;
XRRCrtcInfo *crtcInfo = XRRGetCrtcInfo(disp, screenResources, crtc);
const XRRModeInfo * modeInfo = get_mode_info(screenResources, crtcInfo->mode);
if (!modeInfo)
{
XRRFreeCrtcInfo(crtcInfo);
continue;
}
bool isMirror = false;
for (unsigned int j = 0; j < displayList.Size(); j++)
{
DisplayInfo &otherDisplay = displayList[j];
if (otherDisplay.bounds.x == crtcInfo->x
&& otherDisplay.bounds.y == crtcInfo->y
&& otherDisplay.bounds.w == crtcInfo->width
&& otherDisplay.bounds.h == crtcInfo->height)
{
isMirror = true;
break;
}
}
if (isMirror)
{
XRRFreeCrtcInfo(crtcInfo);
continue;
}
display.id = (DisplayID)crtc;
display.bounds.x = crtcInfo->x;
display.bounds.y = crtcInfo->y;
display.bounds.w = crtcInfo->width;
display.bounds.h = crtcInfo->height;
parse_display_mode(modeInfo, display.mode, crtcInfo->rotation);
display.mode.bits_per_pixel = bpp;
display.mode.scaling_factor = (double)display.mode.width / (double)crtcInfo->width;
display.name = NULL;
if (crtcInfo->noutput > 0)
{
XRROutputInfo *outputInfo = XRRGetOutputInfo(disp, screenResources, crtcInfo->outputs[0]);
if (outputInfo->name)
{
display.name = (char*)malloc(outputInfo->nameLen + 1);
strcpy(display.name, outputInfo->name);
}
XRRFreeOutputInfo(outputInfo);
}
displayList.Push(display);
XRRFreeCrtcInfo(crtcInfo);
}
XRRFreeScreenResources(screenResources);
}
void defos_get_display_modes(DisplayID displayID, dmArray<DisplayModeInfo> &modeList)
{
RRCrtc crtc = (RRCrtc)displayID;
XRRScreenResources *screenResources = XRRGetScreenResourcesCurrent(disp, win);
XRRCrtcInfo *crtcInfo = XRRGetCrtcInfo(disp, screenResources, crtc);
if (crtcInfo->noutput <= 0)
{
XRRFreeCrtcInfo(crtcInfo);
XRRFreeScreenResources(screenResources);
return;
}
RROutput output = crtcInfo->outputs[0];
XRROutputInfo *outputInfo = XRRGetOutputInfo(disp, screenResources, output);
unsigned long bpp = (long)DefaultDepth(disp, screen);
const XRRModeInfo *currentModeInfo = get_mode_info(screenResources, crtcInfo->mode);
double scaling_factor = (double)currentModeInfo->width / (double)(
axis_flipped(crtcInfo->rotation) ? crtcInfo->height : crtcInfo->width
);
modeList.OffsetCapacity(outputInfo->nmode);
for (int i = 0; i < outputInfo->nmode; i++)
{
const XRRModeInfo *modeInfo = get_mode_info(screenResources, outputInfo->modes[i]);
DisplayModeInfo mode;
parse_display_mode(modeInfo, mode, crtcInfo->rotation);
mode.bits_per_pixel = bpp;
mode.scaling_factor = scaling_factor;
modeList.Push(mode);
}
XRRFreeOutputInfo(outputInfo);
XRRFreeScreenResources(screenResources);
}
static RRCrtc get_current_crtc(WinRect &bounds)
{
WinRect viewBounds = defos_get_view_size();
WinRect bestBounds = { 0, 0, 0, 0 };
RRCrtc bestCrtc = 0;
float bestArea = -1.0f;
XRRScreenResources *screenResources = XRRGetScreenResourcesCurrent(disp, win);
for (int i = 0; i < screenResources->ncrtc; i++)
{
RRCrtc crtc = screenResources->crtcs[i];
XRRCrtcInfo *crtcInfo = XRRGetCrtcInfo(disp, screenResources, crtc);
WinRect crtcBounds = { (float)crtcInfo->x, (float)crtcInfo->y, (float)crtcInfo->width, (float)crtcInfo->height };
XRRFreeCrtcInfo(crtcInfo);
if (!crtcBounds.w || !crtcBounds.h) { continue; }
WinRect clip = viewBounds;
if (crtcBounds.x > clip.x)
{
clip.w -= crtcBounds.x - clip.x;
clip.x = crtcBounds.x;
}
if (crtcBounds.y > clip.y)
{
clip.h -= crtcBounds.y - clip.y;
clip.y = crtcBounds.y;
}
if (crtcBounds.x + crtcBounds.w < clip.x + clip.w)
{
clip.w = crtcBounds.x + crtcBounds.w - clip.x;
}
if (crtcBounds.y + crtcBounds.h < clip.y + clip.h)
{
clip.h = crtcBounds.y + crtcBounds.h - clip.y;
}
float area = (clip.w > 0 && clip.h > 0) ? clip.w * clip.h : 0.0f;
if (area > bestArea)
{
bestCrtc = crtc;
bestBounds = crtcBounds;
bestArea = area;
}
}
XRRFreeScreenResources(screenResources);
bounds = bestBounds;
return bestCrtc;
}
DisplayID defos_get_current_display()
{
WinRect bounds;
return (DisplayID)get_current_crtc(bounds);
}
void defos_set_window_icon(const char *icon_path)
{
dmLogWarning("Method 'set_window_icon' is not supported on Linux");
}
static char* copy_string(const char * s)
{
char *newString = (char*)malloc(strlen(s) + 1);
strcpy(newString, s);
return newString;
}
char* defos_get_bundle_root()
{
char* result;
char* path = (char*)malloc(PATH_MAX + 2);
ssize_t ret = readlink("/proc/self/exe", path, PATH_MAX + 2);
if (ret >= 0 && ret <= PATH_MAX + 1) {
result = copy_string(dirname(path));
} else {
const char* path2 = (const char*)getauxval(AT_EXECFN);
if (!path2) {
result = copy_string(".");
} else if (!realpath(path2, path)) {
result = copy_string(".");
} else {
result = copy_string(dirname(path));
}
}
free(path);
return result;
}
static int shared_argc = 0;
static char** shared_argv = NULL;
static void arguments_main_hook(int argc, char* argv[], char* envp[])
{
shared_argc = argc;
shared_argv = argv;
}
__attribute__((section(".init_array"), used))
void (* defos_arguments_main_hook)(int,char*[],char*[]) = &arguments_main_hook;
void defos_get_arguments(dmArray<char*> &arguments)
{
arguments.OffsetCapacity(shared_argc);
for (int i = 0; i < shared_argc; i++)
{
arguments.Push(copy_string(shared_argv[i]));
}
}
//from glfw/x11_window.c
static void send_message(Window& window, Atom type, long a, long b, long c, long d,long e)
static void send_message(Window &window, Atom type, long a, long b, long c, long d, long e)
{
XEvent event;
memset(&event, 0, sizeof(event));
@ -348,13 +830,13 @@ static void send_message(Window& window, Atom type, long a, long b, long c, long
event.xclient.window = window;
event.xclient.format = 32;
event.xclient.message_type = type;
event.xclient.data.l[0]=a;
event.xclient.data.l[1]=b;
event.xclient.data.l[2]=c;
event.xclient.data.l[3]=d;
event.xclient.data.l[4]=e;
XSendEvent(disp, root, False, SubstructureNotifyMask|SubstructureRedirectMask, &event);
event.xclient.data.l[0] = a;
event.xclient.data.l[1] = b;
event.xclient.data.l[2] = c;
event.xclient.data.l[3] = d;
event.xclient.data.l[4] = e;
XSendEvent(disp, root, False, SubstructureNotifyMask | SubstructureRedirectMask, &event);
}
#endif
#endif

View File

@ -5,12 +5,16 @@
#include "defos_private.h"
#include <AppKit/AppKit.h>
#include <IOKit/graphics/IOGraphicsLib.h>
#include <CoreGraphics/CoreGraphics.h>
#include <CoreVideo/CVDisplayLink.h>
static NSWindow* window = nil;
static NSCursor* current_cursor = nil;
static NSCursor* default_cursor = nil;
#define MAX_DISPLAYS 32
static bool is_maximized = false;
static bool is_mouse_in_view = false;
static bool is_cursor_visible = true;
@ -21,6 +25,29 @@ static NSRect previous_state;
static void enable_mouse_tracking();
static void disable_mouse_tracking();
/*
* Convert the mode string to the more convinient bits per pixel value
*/
static int getBPPFromModeString(CFStringRef mode)
{
if ((CFStringCompare(mode, CFSTR(kIO30BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo)) {
// This is a strange mode, where we using 10 bits per RGB component and pack it into 32 bits
// Java is not ready to work with this mode but we have to specify it as supported
return 30;
}
else if (CFStringCompare(mode, CFSTR(IO32BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
return 32;
}
else if (CFStringCompare(mode, CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
return 16;
}
else if (CFStringCompare(mode, CFSTR(IO8BitIndexedPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
return 8;
}
return 0;
}
void defos_init() {
window = dmGraphics::GetNativeOSXNSWindow();
// [window disableCursorRects];
@ -79,6 +106,10 @@ void defos_toggle_maximized() {
}
}
void defos_toggle_always_on_top() {
window.level = defos_is_always_on_top() ? NSNormalWindowLevel : NSFloatingWindowLevel;
}
bool defos_is_fullscreen() {
BOOL fullscreen = (([window styleMask] & NSFullScreenWindowMask) == NSFullScreenWindowMask);
return fullscreen == YES;
@ -88,6 +119,14 @@ bool defos_is_maximized() {
return is_maximized;
}
bool defos_is_always_on_top() {
return window.level == NSFloatingWindowLevel;
}
void defos_minimize() {
[window performMiniaturize: nil];
}
void defos_set_window_title(const char* title_lua) {
NSString* title = [NSString stringWithUTF8String:title_lua];
[window setTitle:title];
@ -110,14 +149,15 @@ char* defos_get_bundle_root() {
return bundlePath_lua;
}
void defos_get_parameters(dmArray<char*>* parameters) {
void defos_get_arguments(dmArray<char*> &arguments) {
NSArray *args = [[NSProcessInfo processInfo] arguments];
for (int i = 0; i < [args count]; i++){
int argCount = [args count];
arguments.OffsetCapacity(argCount);
for (int i = 0; i < argCount; i++){
const char *param = [args[i] UTF8String];
char* lua_param = (char*)malloc(strlen(param) + 1);
strcpy(lua_param, param);
parameters->OffsetCapacity(1);
parameters->Push(lua_param);
arguments.Push(lua_param);
}
}
@ -188,7 +228,7 @@ WinRect defos_get_view_size() {
}
void defos_set_console_visible(bool visible) {
dmLogInfo("Method 'defos_set_console_visible' is not supported in macOS");
dmLogWarning("Method 'set_console_visible' is only supported on Windows");
}
bool defos_is_console_visible() {
@ -199,28 +239,51 @@ void defos_set_cursor_visible(bool visible) {
if (is_cursor_visible == visible) { return; }
is_cursor_visible = visible;
if (visible) {
[NSCursor unhide];
[NSCursor unhide];
} else {
[NSCursor hide];
[NSCursor hide];
}
}
bool defos_is_cursor_visible() {
return is_cursor_visible;
return is_cursor_visible;
}
bool defos_is_mouse_in_view() {
return is_mouse_in_view;
}
WinPoint defos_get_cursor_pos() {
NSPoint point = NSEvent.mouseLocation;
WinPoint result;
result.x = (float)point.x;
result.y = (float)NSMaxY(NSScreen.screens[0].frame) - point.y;
return result;
}
WinPoint defos_get_cursor_pos_view() {
NSView* view = dmGraphics::GetNativeOSXNSView();
NSPoint pointInScreen = NSEvent.mouseLocation;
NSPoint windowOrigin = window.frame.origin;
NSPoint pointInWindow = NSMakePoint(
pointInScreen.x - windowOrigin.x,
pointInScreen.y - windowOrigin.y
);
NSPoint point = [view convertPoint: pointInWindow fromView: nil];
WinPoint result;
result.x = (float)point.x;
result.y = (float)(view.bounds.size.height - point.y);
return result;
}
void defos_set_cursor_pos(float x, float y) {
CGWarpMouseCursorPosition(CGPointMake(x, y));
if (!is_cursor_locked) {
CGAssociateMouseAndMouseCursorPosition(true); // Prevents a delay after the Wrap call
CGAssociateMouseAndMouseCursorPosition(true); // Prevents a delay after the Wrap call
}
}
void defos_move_cursor_to(float x, float y) {
void defos_set_cursor_pos_view(float x, float y) {
NSView* view = dmGraphics::GetNativeOSXNSView();
NSPoint pointInWindow = [view convertPoint: NSMakePoint(x, view.bounds.size.height - y) toView: nil];
NSPoint windowOrigin = window.frame.origin;
@ -262,7 +325,7 @@ static void clip_cursor(NSEvent * event) {
}
if (willClip) {
defos_move_cursor_to(mousePos.x, bounds.size.height - mousePos.y);
defos_set_cursor_pos_view(mousePos.x, bounds.size.height - mousePos.y);
}
}
@ -326,6 +389,224 @@ void defos_reset_cursor() {
current_cursor = default_cursor;
}
static DisplayModeInfo parse_mode(CGDisplayModeRef mode, CVDisplayLinkRef displayLink, double rotation) {
DisplayModeInfo mode_info;
mode_info.width = CGDisplayModeGetPixelWidth(mode);
mode_info.height = CGDisplayModeGetPixelHeight(mode);
mode_info.scaling_factor = (double)mode_info.width / (double)CGDisplayModeGetWidth(mode);
mode_info.refresh_rate = CGDisplayModeGetRefreshRate(mode);
CFStringRef pixelEncoding = CGDisplayModeCopyPixelEncoding(mode);
mode_info.bits_per_pixel = getBPPFromModeString(pixelEncoding);
CFRelease(pixelEncoding);
mode_info.orientation = (unsigned long)rotation;
mode_info.reflect_x = false;
mode_info.reflect_y = false;
if (mode_info.refresh_rate == 0) {
const CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(displayLink);
if (!(time.flags & kCVTimeIsIndefinite)) {
mode_info.refresh_rate = (double)time.timeScale / (double)time.timeValue;
}
}
return mode_info;
}
static io_service_t IOServicePortFromCGDisplayID(CGDirectDisplayID displayID) {
io_iterator_t iter;
io_service_t serv, servicePort = 0;
CFMutableDictionaryRef matching = IOServiceMatching("IODisplayConnect");
// releases matching for us
kern_return_t err = IOServiceGetMatchingServices(kIOMasterPortDefault, matching, &iter);
if (err) { return 0; }
while ((serv = IOIteratorNext(iter)) != 0) {
CFDictionaryRef displayInfo;
CFNumberRef vendorIDRef;
CFNumberRef productIDRef;
CFNumberRef serialNumberRef;
displayInfo = IODisplayCreateInfoDictionary(serv, kIODisplayOnlyPreferredName);
Boolean success;
success = CFDictionaryGetValueIfPresent(displayInfo, CFSTR(kDisplayVendorID), (const void**)&vendorIDRef);
success &= CFDictionaryGetValueIfPresent(displayInfo, CFSTR(kDisplayProductID), (const void**)&productIDRef);
if (!success) {
CFRelease(displayInfo);
continue;
}
SInt32 vendorID;
CFNumberGetValue(vendorIDRef, kCFNumberSInt32Type, &vendorID);
SInt32 productID;
CFNumberGetValue(productIDRef, kCFNumberSInt32Type, &productID);
// If a serial number is found, use it.
// Otherwise serial number will be nil (= 0) which will match with the output of 'CGDisplaySerialNumber'
SInt32 serialNumber = 0;
if (CFDictionaryGetValueIfPresent(displayInfo, CFSTR(kDisplaySerialNumber), (const void**)&serialNumberRef)) {
CFNumberGetValue(serialNumberRef, kCFNumberSInt32Type, &serialNumber);
}
// If the vendor and product id along with the serial don't match
// then we are not looking at the correct monitor.
// NOTE: The serial number is important in cases where two monitors
// are the exact same.
if (CGDisplayVendorNumber(displayID) != vendorID ||
CGDisplayModelNumber(displayID) != productID ||
CGDisplaySerialNumber(displayID) != serialNumber ) {
CFRelease(displayInfo);
continue;
}
servicePort = serv;
CFRelease(displayInfo);
break;
}
IOObjectRelease(iter);
return servicePort;
}
static char* get_display_name(CGDirectDisplayID displayID) {
NSString *screenName = nil;
NSDictionary *deviceInfo = (NSDictionary *)IODisplayCreateInfoDictionary(IOServicePortFromCGDisplayID(displayID), kIODisplayOnlyPreferredName);
NSDictionary *localizedNames = [deviceInfo objectForKey:[NSString stringWithUTF8String:kDisplayProductName]];
if ([localizedNames count] > 0) {
NSString *screenName = [localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]];
size_t nameLength = [screenName lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 1;
char *name = (char*)malloc(nameLength);
[screenName getCString:name maxLength:nameLength encoding:NSUTF8StringEncoding];
[deviceInfo release];
return name;
}
[deviceInfo release];
return NULL;
}
void defos_get_displays(dmArray<DisplayInfo> &displayList){
uint32_t numDisplays;
CGDirectDisplayID displays[MAX_DISPLAYS];
CGGetActiveDisplayList(MAX_DISPLAYS, displays, &numDisplays);
displayList.OffsetCapacity(numDisplays);
for (int i = 0; i < numDisplays; i++) {
CGDirectDisplayID displayID = displays[i];
// We don't report mirrored displays
if (CGDisplayIsInMirrorSet(displayID) && CGDisplayPrimaryDisplay(displayID) != displayID) {
continue;
}
DisplayInfo display;
display.id = (void*)(size_t)displayID;
CGRect bounds = CGDisplayBounds(displayID);
display.bounds.x = bounds.origin.x;
display.bounds.y = bounds.origin.y;
display.bounds.w = bounds.size.width;
display.bounds.h = bounds.size.height;
CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displayID);
CVDisplayLinkRef displayLink;
CVDisplayLinkCreateWithCGDisplay(displayID, &displayLink);
double rotation = CGDisplayRotation(displayID);
display.mode = parse_mode(mode, displayLink, rotation);
CVDisplayLinkRelease(displayLink);
CGDisplayModeRelease(mode);
display.name = get_display_name(displayID);
displayList.Push(display);
}
}
void defos_get_display_modes(DisplayID displayID_, dmArray<DisplayModeInfo> &modeList) {
CGDirectDisplayID displayID = (CGDirectDisplayID)(size_t)displayID_;
NSDictionary *optDict = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithBool: YES], kCGDisplayShowDuplicateLowResolutionModes, nil
];
CFArrayRef modes = CGDisplayCopyAllDisplayModes(displayID, (__bridge CFDictionaryRef)optDict);
[optDict release];
// Prepend the current display mode
int modeCount = CFArrayGetCount(modes) + 1;
CGDisplayModeRef* allModes = new CGDisplayModeRef[modeCount];
allModes[0] = CGDisplayCopyDisplayMode(displayID);
for (int i = 1; i < modeCount; i++) {
allModes[i] = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i - 1);
}
// Make a display link for refresh rate detection fallback
CVDisplayLinkRef displayLink;
CVDisplayLinkCreateWithCGDisplay(displayID, &displayLink);
double rotation = CGDisplayRotation(displayID);
modeList.OffsetCapacity(modeCount);
for (int i = 0; i < modeCount; i++) {
CGDisplayModeRef mode = allModes[i];
DisplayModeInfo modeInfo = parse_mode(mode, displayLink, rotation);
// Remove duplicates
size_t width = CGDisplayModeGetWidth(mode);
size_t height = CGDisplayModeGetHeight(mode);
size_t pixelWidth = modeInfo.width;
size_t pixelHeight = modeInfo.height;
double refreshRate = CGDisplayModeGetRefreshRate(mode);
CFStringRef pixelEncoding = CGDisplayModeCopyPixelEncoding(mode);
bool shouldAdd = true;
for (int j = 0; j < i; j++) {
CGDisplayModeRef otherMode = allModes[j];
if (CFEqual(mode, otherMode)) {
shouldAdd = false;
break;
}
size_t otherWidth = CGDisplayModeGetWidth(otherMode);
size_t otherHeight = CGDisplayModeGetHeight(otherMode);
size_t otherPixelWidth = CGDisplayModeGetPixelWidth(otherMode);
size_t otherPixelHeight = CGDisplayModeGetPixelHeight(otherMode);
double otherRefreshRate = CGDisplayModeGetRefreshRate(otherMode);
CFStringRef otherPixelEncoding = CGDisplayModeCopyPixelEncoding(otherMode);
bool samePixelEncoding = (CFStringCompare(pixelEncoding, otherPixelEncoding, 0) == kCFCompareEqualTo);
CFRelease(pixelEncoding);
CFRelease(otherPixelEncoding);
if (samePixelEncoding
&& pixelWidth == otherPixelWidth
&& pixelHeight == otherPixelHeight
&& width == otherWidth
&& height == otherHeight
&& refreshRate == otherRefreshRate
) {
shouldAdd = false;
break;
}
}
if (shouldAdd) { modeList.Push(modeInfo); }
}
CVDisplayLinkRelease(displayLink);
CGDisplayModeRelease(allModes[0]);
CFRelease(modes);
}
DisplayID defos_get_current_display() {
return (void*)(size_t)[[[[window screen] deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue];
}
@interface DefOSMouseTracker : NSResponder
@end
@implementation DefOSMouseTracker

View File

@ -4,7 +4,11 @@
#include <math.h>
struct WinRect {
float x,y,w,h;
float x, y, w, h;
};
struct WinPoint {
float x, y;
};
struct LuaCallbackInfo {
@ -29,6 +33,30 @@ typedef enum {
DEFOS_CURSOR_IBEAM,
} DefosCursor;
#ifdef DM_PLATFORM_WINDOWS
typedef const char* DisplayID;
#else
typedef void* DisplayID;
#endif
struct DisplayModeInfo {
unsigned long width;
unsigned long height;
unsigned long bits_per_pixel;
double refresh_rate;
double scaling_factor;
unsigned long orientation;
bool reflect_x;
bool reflect_y;
};
struct DisplayInfo {
DisplayID id;
struct WinRect bounds;
struct DisplayModeInfo mode;
char * name;
};
extern LuaCallbackInfo defos_event_handlers[];
extern void defos_emit_event(DefosEvent event);
extern void defos_event_handler_was_set(DefosEvent event);
@ -46,13 +74,16 @@ extern void defos_disable_window_resize();
extern void defos_toggle_fullscreen();
extern void defos_toggle_maximized();
extern void defos_toggle_always_on_top();
extern bool defos_is_fullscreen();
extern bool defos_is_maximized();
extern bool defos_is_always_on_top();
extern void defos_minimize();
extern void defos_set_window_title(const char* title_lua);
extern void defos_set_window_icon(const char *icon_path);
extern char* defos_get_bundle_root();
extern void defos_get_parameters(dmArray<char*>* parameters);
extern void defos_get_arguments(dmArray<char*> &arguments);
extern void defos_set_window_size(float x, float y, float w, float h);
extern WinRect defos_get_window_size();
@ -67,7 +98,9 @@ extern bool defos_is_cursor_visible();
extern bool defos_is_mouse_in_view();
extern void defos_set_cursor_pos(float x, float y);
extern void defos_move_cursor_to(float x, float y);
extern void defos_set_cursor_pos_view(float x, float y);
extern WinPoint defos_get_cursor_pos();
extern WinPoint defos_get_cursor_pos_view();
extern void defos_set_cursor_clipped(bool clipped);
extern bool defos_is_cursor_clipped();
@ -79,4 +112,8 @@ extern void defos_set_custom_cursor_win(const char *filename);
extern void defos_set_custom_cursor_mac(dmBuffer::HBuffer buffer, float hotSpotX, float hotSpotY);
extern void defos_set_custom_cursor_linux(const char *filename);
extern void defos_set_cursor(DefosCursor cursor);
extern void defos_reset_cursor();
extern void defos_reset_cursor();
extern void defos_get_displays(dmArray<DisplayInfo> &displayList);
extern void defos_get_display_modes(DisplayID displayID, dmArray<DisplayModeInfo> &modeList);
extern DisplayID defos_get_current_display();

View File

@ -23,6 +23,8 @@ static bool is_cursor_clipped = false;
static POINT lock_point;
static bool is_cursor_locked = false;
static bool is_window_on_top = false;
static bool is_window_active = true;
static bool is_cursor_visible = true;
static bool is_custom_cursor_loaded;
static HCURSOR custom_cursor;
@ -41,6 +43,8 @@ void subclass_window();
void defos_init()
{
is_window_active = true;
is_window_on_top = false;
is_mouse_inside = false;
is_cursor_clipped = false;
GetClipCursor(&originalRect); // keep the original clip for restore
@ -153,6 +157,26 @@ void defos_toggle_maximized()
}
}
void defos_toggle_always_on_top()
{
is_window_on_top = !is_window_on_top;
HWND window = dmGraphics::GetNativeWindowsHWND();
SetWindowPos(window,
is_window_on_top ? HWND_TOPMOST : HWND_NOTOPMOST,
0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE
);
}
bool defos_is_always_on_top() {
return is_window_on_top;
}
void defos_minimize()
{
HWND window = dmGraphics::GetNativeWindowsHWND();
ShowWindow(window, SW_MINIMIZE);
}
void defos_set_console_visible(bool visible)
{
::ShowWindow(::GetConsoleWindow(), visible ? SW_SHOW : SW_HIDE);
@ -165,28 +189,33 @@ bool defos_is_console_visible()
void defos_set_window_size(float x, float y, float w, float h)
{
if (isnan(x))
HWND window = dmGraphics::GetNativeWindowsHWND();
if (isnan(x) || isnan(y))
{
x = (GetSystemMetrics(SM_CXSCREEN) - w) / 2;
}
if (isnan(y))
{
y = (GetSystemMetrics(SM_CYSCREEN) - h) / 2;
HMONITOR hMonitor = MonitorFromWindow(window, MONITOR_DEFAULTTONEAREST);
MONITORINFO monitorInfo;
monitorInfo.cbSize = sizeof(monitorInfo);
GetMonitorInfo(hMonitor, &monitorInfo);
if (isnan(x)) { x = (monitorInfo.rcMonitor.left + monitorInfo.rcMonitor.right - w) / 2; }
if (isnan(y)) { y = (monitorInfo.rcMonitor.top + monitorInfo.rcMonitor.bottom - h) / 2; }
}
HWND window = dmGraphics::GetNativeWindowsHWND();
SetWindowPos(window, window, (int)x, (int)y, (int)w, (int)h, SWP_NOZORDER);
}
void defos_set_view_size(float x, float y, float w, float h)
{
if (isnan(x))
HWND window = dmGraphics::GetNativeWindowsHWND();
if (isnan(x) || isnan(y))
{
x = (GetSystemMetrics(SM_CXSCREEN) - w) / 2;
}
if (isnan(y))
{
y = (GetSystemMetrics(SM_CYSCREEN) - h) / 2;
HMONITOR hMonitor = MonitorFromWindow(window, MONITOR_DEFAULTTONEAREST);
MONITORINFO monitorInfo;
monitorInfo.cbSize = sizeof(monitorInfo);
GetMonitorInfo(hMonitor, &monitorInfo);
if (isnan(x)) { x = (monitorInfo.rcMonitor.left + monitorInfo.rcMonitor.right - w) / 2; }
if (isnan(y)) { y = (monitorInfo.rcMonitor.top + monitorInfo.rcMonitor.bottom - h) / 2; }
}
RECT rect = {0, 0, (int)w, (int)h};
@ -196,8 +225,6 @@ void defos_set_view_size(float x, float y, float w, float h)
// TODO: we are assuming the window have no menu, maybe it is better to expose it as parameter later
AdjustWindowRect(&rect, style, false);
HWND window = dmGraphics::GetNativeWindowsHWND();
SetWindowPos(window, window, (int)x, (int)y, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
}
@ -235,19 +262,19 @@ char* defos_get_bundle_root() {
return bundlePath;
}
void defos_get_parameters(dmArray<char*>* parameters) {
void defos_get_arguments(dmArray<char*> &arguments) {
LPWSTR *szArglist;
int nArgs;
int i;
szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
if( NULL != szArglist ){
arguments.OffsetCapacity(nArgs);
for( i=0; i<nArgs; i++) {
const wchar_t *param = szArglist[i];
int len = wcslen(param) + 1;
char* lua_param = (char*)malloc(len);
wcstombs(lua_param, param, len);
parameters->OffsetCapacity(1);
parameters->Push(lua_param);
arguments.Push(lua_param);
}
}
LocalFree(szArglist);
@ -256,14 +283,15 @@ void defos_get_parameters(dmArray<char*>* parameters) {
WinRect defos_get_window_size()
{
HWND window = dmGraphics::GetNativeWindowsHWND();
WINDOWPLACEMENT frame = {sizeof(placement)};
GetWindowPlacement(window, &frame);
WinRect rect;
rect.x = (float)frame.rcNormalPosition.left;
rect.y = (float)frame.rcNormalPosition.top;
rect.w = (float)(frame.rcNormalPosition.right - frame.rcNormalPosition.left);
rect.h = (float)(frame.rcNormalPosition.bottom - frame.rcNormalPosition.top);
return rect;
RECT rect;
GetWindowRect(window, &rect);
WinRect result = {
.x = rect.left,
.y = rect.top,
.w = rect.right - rect.left,
.h = rect.bottom - rect.top,
};
return result;
}
WinRect defos_get_view_size()
@ -276,16 +304,36 @@ WinRect defos_get_view_size()
POINT pos = {wrect.left, wrect.top};
ClientToScreen(window, &pos);
WINDOWPLACEMENT frame = {sizeof(placement)};
GetWindowPlacement(window, &frame);
WinRect rect;
rect.x = (float)pos.x;
rect.y = (float)pos.y;
rect.w = (float)(wrect.right - wrect.left);
rect.h = (float)(wrect.bottom - wrect.top);
WinRect rect = {
.x = pos.x,
.y = pos.y,
.w = wrect.right - wrect.left,
.h = wrect.bottom - wrect.top,
};
return rect;
}
WinPoint defos_get_cursor_pos()
{
POINT point;
GetCursorPos(&point);
WinPoint result = { .x = point.x, .y = point.y };
return result;
}
WinPoint defos_get_cursor_pos_view()
{
POINT point;
GetCursorPos(&point);
HWND window = dmGraphics::GetNativeWindowsHWND();
ScreenToClient(window, &point);
WinPoint result = { .x = point.x, .y = point.y };
return result;
}
void defos_set_cursor_pos(float x, float y)
{
SetCursorPos((int)x, (int)y);
@ -293,7 +341,7 @@ void defos_set_cursor_pos(float x, float y)
// move cursor to pos relative to current window
// top-left is (0, 0)
void defos_move_cursor_to(float x, float y)
void defos_set_cursor_pos_view(float x, float y)
{
HWND window = dmGraphics::GetNativeWindowsHWND();
@ -357,7 +405,7 @@ bool defos_is_cursor_locked()
}
void defos_update() {
if (is_cursor_locked) {
if (is_cursor_locked && is_window_active) {
SetCursorPos(lock_point.x, lock_point.y);
}
}
@ -387,6 +435,151 @@ void defos_reset_cursor()
is_custom_cursor_loaded = false;
}
static char* copy_string(const char *s)
{
char *buffer = (char*)malloc(strlen(s) + 1);
strcpy(buffer, s);
return buffer;
}
static unsigned long translate_orientation(DWORD orientation)
{
switch (orientation)
{
case DMDO_DEFAULT: return 0;
case DMDO_90: return 90;
case DMDO_180: return 180;
case DMDO_270: return 270;
default: return 0;
}
}
static void parse_display_mode(const DEVMODE &devMode, DisplayModeInfo &mode)
{
mode.width = devMode.dmPelsWidth;
mode.height = devMode.dmPelsHeight;
mode.bits_per_pixel = devMode.dmBitsPerPel;
mode.refresh_rate = devMode.dmDisplayFrequency;
mode.scaling_factor = 1.0;
mode.orientation = (devMode.dmFields & DM_DISPLAYORIENTATION)
? translate_orientation(devMode.dmDisplayOrientation)
: 0;
mode.reflect_x = false;
mode.reflect_y = false;
}
static BOOL CALLBACK monitor_enum_callback(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData)
{
MONITORINFOEX monitorInfo;
monitorInfo.cbSize = sizeof(monitorInfo);
if (!GetMonitorInfo(hMonitor, &monitorInfo)) { return TRUE; }
DisplayInfo display;
display.id = copy_string(monitorInfo.szDevice);
display.bounds.x = monitorInfo.rcMonitor.left;
display.bounds.y = monitorInfo.rcMonitor.top;
display.bounds.w = monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left;
display.bounds.h = monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top;
DEVMODE devMode;
devMode.dmSize = sizeof(devMode);
EnumDisplaySettingsEx(monitorInfo.szDevice, ENUM_CURRENT_SETTINGS, &devMode, 0);
parse_display_mode(devMode, display.mode);
display.mode.scaling_factor = (double)devMode.dmPelsWidth / (double)(monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left);
DISPLAY_DEVICE displayDevice;
displayDevice.cb = sizeof(displayDevice);
EnumDisplayDevices(monitorInfo.szDevice, 0, &displayDevice, 0);
display.name = copy_string(displayDevice.DeviceString);
dmArray<DisplayInfo> *displayList = reinterpret_cast<dmArray<DisplayInfo>*>(dwData);
displayList->OffsetCapacity(1);
displayList->Push(display);
return TRUE;
}
void defos_get_displays(dmArray<DisplayInfo> &displayList)
{
EnumDisplayMonitors(NULL, NULL, monitor_enum_callback, reinterpret_cast<LPARAM>(&displayList));
}
struct MonitorScaleData {
const char *display_device_name;
double scaling_factor;
};
static BOOL CALLBACK monitor_scale_enum_callback(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData)
{
MonitorScaleData *data = reinterpret_cast<MonitorScaleData*>(dwData);
MONITORINFOEX monitorInfo;
monitorInfo.cbSize = sizeof(monitorInfo);
if (!GetMonitorInfo(hMonitor, &monitorInfo)) { return TRUE; }
if (strcmp(monitorInfo.szDevice, data->display_device_name) != 0) { return TRUE; }
DEVMODE devMode;
devMode.dmSize = sizeof(devMode);
EnumDisplaySettings(monitorInfo.szDevice, ENUM_CURRENT_SETTINGS, &devMode);
data->scaling_factor = (double)devMode.dmPelsWidth / (double)(monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left);
return FALSE;
}
static double scale_of_monitor(DisplayID displayID)
{
MonitorScaleData data = {
.display_device_name = displayID,
.scaling_factor = 1.0,
};
EnumDisplayMonitors(NULL, NULL, monitor_scale_enum_callback, reinterpret_cast<LPARAM>(&data));
return data.scaling_factor;
}
void defos_get_display_modes(DisplayID displayID, dmArray<DisplayModeInfo> &modeList)
{
DEVMODE devMode = {};
devMode.dmSize = sizeof(devMode);
double scaling_factor = scale_of_monitor(displayID);
for (int i = 0; EnumDisplaySettingsEx(displayID, i, &devMode, 0) != 0; i++)
{
DisplayModeInfo mode;
parse_display_mode(devMode, mode);
mode.scaling_factor = scaling_factor;
bool isDuplicate = false;
for (int j = (int)modeList.Size() - 1; j >= 0; j--)
{
DisplayModeInfo &otherMode = modeList[j];
if (mode.width == otherMode.width
&& mode.height == otherMode.height
&& mode.bits_per_pixel == otherMode.bits_per_pixel
&& mode.refresh_rate == otherMode.refresh_rate
) {
isDuplicate = true;
break;
}
}
if (isDuplicate) { continue; }
modeList.OffsetCapacity(1);
modeList.Push(mode);
}
}
DisplayID defos_get_current_display()
{
HWND window = dmGraphics::GetNativeWindowsHWND();
HMONITOR hMonitor = MonitorFromWindow(window, MONITOR_DEFAULTTONEAREST);
MONITORINFOEX monitorInfo;
monitorInfo.cbSize = sizeof(monitorInfo);
GetMonitorInfo(hMonitor, &monitorInfo);
return copy_string(monitorInfo.szDevice);
}
/********************
* internal functions
********************/
@ -478,12 +671,14 @@ static LRESULT __stdcall custom_wndproc(HWND hwnd, UINT umsg, WPARAM wp, LPARAM
}
break;
case WM_SIZE:
if (is_cursor_locked)
case WM_ACTIVATE:
if (wp != WA_INACTIVE)
{
defos_set_cursor_locked(true);
is_window_active = true;
if (is_cursor_clipped) { defos_set_cursor_clipped(true); }
} else {
is_window_active = false;
}
break;
}
if (originalProc != NULL)

View File

@ -2892,6 +2892,68 @@ nodes {
text_leading: 1.0
text_tracking: 0.0
}
nodes {
position {
x: 367.0
y: 270.066
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 700.0
y: 100.0
z: 0.0
w: 1.0
}
color {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
type: TYPE_TEXT
blend_mode: BLEND_MODE_ALPHA
text: "cursor position"
font: "larryfont"
id: "cursor_pos"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_NW
outline {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
shadow {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
adjust_mode: ADJUST_MODE_FIT
line_break: false
layer: ""
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
shadow_alpha: 1.0
template_node_child: false
text_leading: 1.0
text_tracking: 0.0
}
material: "/builtins/materials/gui.material"
adjust_reference: ADJUST_REFERENCE_LEGACY
max_nodes: 512

View File

@ -11,25 +11,6 @@ local ICON_NAMES = {
["Darwin"] = "mac.png"
}
function window_callback(self, event, data)
if event == window.WINDOW_EVENT_FOCUS_LOST then
-- though after lost focus cursor clipping will restore, we should restore it
if self.cursor_clipped then
defos.set_cursor_clipped(false)
end
if self.cursor_locked then
defos.set_cursor_locked(false)
end
elseif event == window.WINDOW_EVENT_FOCUS_GAINED then
if self.cursor_clipped then
defos.set_cursor_clipped(true)
end
if self.cursor_locked then
defos.set_cursor_locked(true)
end
end
end
function toggle_cursor_lock(self)
self.cursor_locked = not self.cursor_locked
defos.set_cursor_locked(self.cursor_locked)
@ -68,7 +49,7 @@ function init(self)
-- NOTE: cursor should be normal x11 cursor file that type is: image/x-xcursor
table.insert(self.cursors, extract_to_savefolder("cursor.xcur"))
end
if system_name == "Windows" then
for i, v in ipairs({"cursor_01.ani", "cursor_02.ani" }) do
-- load source file and write them to save folder, so that we can access them with fullpath, or you can use bundle_resource
@ -129,8 +110,27 @@ function init(self)
defos.set_console_visible(not defos.is_console_visible())
end
window.set_listener(window_callback)
pprint(defos.get_parameters())
local displays = defos.get_displays()
print("Found " .. #displays .. " displays:")
for i, display in ipairs(displays) do
pprint(display)
end
local current_display_id = defos.get_current_display_id()
print("Current display id: ", current_display_id)
local modes = defos.get_display_modes(current_display_id)
print("Found " .. #modes .. " modes for current display:")
for i, mode in ipairs(modes) do
print(
mode.width .. "x" .. mode.height .. " " ..
mode.bits_per_pixel .. "bit @" .. mode.refresh_rate .. "Hz x" ..
mode.scaling_factor .. " " .. mode.orientation .. "deg"
)
end
print("App command line arguments:")
pprint(defos.get_arguments())
end
local function change_mouse_label(text)
@ -143,6 +143,9 @@ function update(self, dt)
gui.set_text(gui.get_node("window_pos"),"window position "..x.." "..y.." "..w.." "..h)
x,y,w,h = defos.get_view_size()
gui.set_text(gui.get_node("view_pos"),"view position "..x.." "..y.." "..w.." "..h)
x, y = defos.get_cursor_pos()
w, h = defos.get_cursor_pos_view()
gui.set_text(gui.get_node("cursor_pos"),"cursor: "..math.floor(x).." "..math.floor(y).." view: "..math.floor(w).." "..math.floor(h))
gui.set_text(gui.get_node("is_fullscreen"),"is_fullscreen "..tostring(defos.is_fullscreen()))
gui.set_text(gui.get_node("is_maximized"),"is_maximized "..tostring(defos.is_maximized()))
gui.set_text(gui.get_node("is_mouse_in_view"),"is_mouse_in_view "..tostring(defos.is_mouse_in_view()))
@ -182,8 +185,7 @@ function on_input(self, action_id, action)
end)
dirtylarry:button("set_window_size", action_id, action, function ()
defos.set_window_size(nil, nil, 1024, 768)
--defos.set_view_size(0, 0, 800, 600)
defos.set_view_size(nil, nil, 1024, 768)
end)
dirtylarry:button("toggle_fullscreen", action_id, action, function ()
@ -208,7 +210,7 @@ function on_input(self, action_id, action)
dirtylarry:button("random_cursor_pos", action_id, action, function()
local x = math.random(1,2000)
local y = math.random(1,2000)
defos.move_cursor_to(x, y)
defos.set_cursor_pos_view(x, y)
end)
dirtylarry:button("clip_cursor", action_id, action, function()
@ -236,8 +238,4 @@ function on_input(self, action_id, action)
defos.set_cursor(self.cursors[self.current_cursor])
end)
if self.cursor_locked and not action_id then
print("FPS mouse dx, dy: ", action.dx, action.dy)
end
end

View File

@ -15,6 +15,7 @@ game_binding = /input/game.input_bindingc
[display]
width = 1024
height = 768
high_dpi = 1
[physics]
scale = 0.02