Windows' windows

This is a "cheat sheet" for "winwin" extension by YellowAfterlife.

The extension can be found on itch.io.

The source code can be found on GitHub.

Click on sections to expand/collapse them.
Quick display controls: Categories · Sections · Everything ·

Getting started
Basics
Basics
winwin_update()

Call this once a frame!


winwin_create(x, y, width, height, config)​

Creates a new window!

xywh are in screen coordinates.

config is a winwin_config.

For example,

var config = new winwin_config();
config.caption = "Hello!";
config.resize = true;
window = winwin_create(window_get_x() - 400, window_get_y(), 350, 600, config);
winwin_destroy(window)

Destroys a previously created window.

winwin_exists(window)​

Returns whether the specified window exists.

This also checks whether the passed reference is a window at all (e.g. not an undefined).


winwin_config()
winwin_config()

This struct holds a variety of properties that can be set when creating new windows.

You can create one using new winwin_config() and fill out the desired variables.

caption: string

Shown in the window's title bar, if visible.

kind: int
resize: bool

Whether the window can be resized (defaults to false).

show: bool

Whether the window should be shown immediately after creation.

Setting this to false can be convenient if you're going to do additional setup before showing the window to the user yourself (using winwin_set_visible).

Defaults to true.

topmost

Whether the window should be marked as stay-on-top.

Same effect as winwin_set_topmost.

Defaults to false.

taskbar_button

Whether the window should have a taskbar shown for it.

Same effect as winwin_set_taskbar_button_visible.

Currently only works for borderless windows since tool windows can't have a taskbar button and regular windows require additional setup for this.

Defaults to true.

clickthrough
noactivate

Whether the window should not be possible to activate/focus.

Same effect as winwin_set_noactivate.

Defaults to false.

per_pixel_alpha
close_button

Whether the window's close button should be enabled and what it should do.

Same effect as winwin_set_close_button.

Defaults to 1.

vsync

Indicates the window's vertical synchronization state.

Same effect as winwin_set_vsync.

Defaults to 0.

thread

Whether the window should do its message handling on a different thread.

This takes marginally more resources and might have situational implications (because the window is no longer owned by the thread where your game code and extensions run), but this prevents the game window from freezing while the additional windows are being dragged around.

It is not possible to make winwin_main threaded, but you could use Gameframe or hide your main window and draw the game to another one.

Defaults to false.

owner
winwin_main

This is a reference to your main game window.

It enables using a handful of extension functions on it, with some remarks:

  • Input functions just call the respective GM functions since GM is responsible for input handling on the game window.
  • Drawing functions (like winwin_draw_begin) don't really work and don't have to be used either.
  • Cursor functions don't work, but you can use the built-in functions (and/or Native Cursors).
Drawing
Drawing
winwin_draw_begin(window)​

Starts drawing to the specified window.

By most means this is like surface_set_target, but for windows.

Returns whether successful. Can fail if you're already drawing to another window.

winwin_draw_clear(color, alpha = 1)

The built-in draw_clear and draw_clear_alpha functions cannot draw to additional windows because they respect the game's original drawing bounds, so I'm giving you this helper function for clearing the window to whatever color and opacity.

winwin_draw_sync()​

If you're doing a surface_set_target while drawing to an additional window, call this after surface_reset_target to make GM draw to that window window again (otherwise the subsequent drawing operations will end up in the main window instead).

Returns whether successful. Returns false if you're not drawing to a window.

winwin_draw_end()​

Stops drawing to the specified window and updates its contents.

This happens immediately and can be used to display debug information in additional windows while the game is busy (e.g. generating a level).

Returns whether successful. Returns false if you're not drawing to a window.

A typical use of drawing functions might look as following:

winwin_draw_begin(my_window);
winwin_draw_clear(c_white);
draw_set_color(c_black);
draw_text(5, 5, "Hello!");
winwin_draw_end();
winwin_resize_buffer(window, width, height)​

Changes internal drawing buffer size of a window.

You shouldn't have to call this explicitly since it'll be called for you a few frames after a window's size changes.

Returns whether successful.

Known issues

I do not go through the trouble of completely clearing-restoring the drawing state, so there are occasional opportunities for self-sabotage if you are setting your own matrices / clip rectangles / etc.

Input
Input

To be fair, you could just use keyboard_check_direct as a replacement for most of these, but where's the fun in that?

Keyboard
Keyboard
winwin_keyboard_check(window, key)​
winwin_keyboard_check_pressed(window, key)​
winwin_keyboard_check_released(window, key)​

Text entry:

winwin_keyboard_get_string(window)​

Like keyboard_string, but for additional windows.

The behaviour should generally be identical to how the built-in variable works, but without erasing it when the window loses focus (why's that a thing, anyway?).

winwin_keyboard_set_string(window, string)​

Changes the current keyboard string for a window.

Settings:

winwin_keyboard_get_max_string_length(window)​

Returns how long a keyboard_string of the given window is allowed to get, in characters.

Default is 128.

winwin_keyboard_set_max_string_length(window)​

Changes how long a keyboard_string of the given window is allowed to get, in characters.

Returns whether successful.

Mouse
Mouse
winwin_mouse_is_over(window)​

Returns whether the cursor is currently over the given window.

winwin_mouse_get_x(window)​int

Like window_mouse_get_x, but for additional windows.

winwin_mouse_get_y(window)​int

Like window_mouse_get_y, but for additional windows.

Buttons:

winwin_mouse_check_button(window, button)​
winwin_mouse_check_button_pressed(window, button)​
winwin_mouse_check_button_released(window, button)​

Wheel:

winwin_mouse_wheel_up(window)​
winwin_mouse_wheel_down(window)​
winwin_mouse_wheel_get_delta_x(window)​int

Returns horizontal wheel delta since last frame.

Per Microsoft documentation, 120 units should be equivalent to one wheel tick.

winwin_mouse_wheel_get_delta_y(window)​int

Returns vertical wheel delta since last frame.

Per Microsoft documentation, 120 units should be equivalent to one wheel tick.

Gamepad

Doing gamepad polling for each window would be both high-effort and computationally taxing, but you can use extensions to poll gamepad input regardless of what window has focus (XInput, DirectInput)

Position and size
Position and size

These return the current position/size of a window.
They'll return undefined if the window has been closed or cannot be measured.

winwin_get_x(window)​
winwin_get_y(window)​
winwin_get_width(window)​
winwin_get_height(window)​

These change position/size of a window:

winwin_set_position(window, x, y)​
winwin_set_size(window, width, height)​
winwin_set_rectangle(window, x, y, width, height)​

These let you change min/max size of a window.
Pass undefined instead of a size to not restrict:

window_set_min_width(window, ?min_width?)​
window_set_min_height(window, ?min_height?)​
window_set_max_width(window, ?max_width?)​
window_set_max_height(window, ?max_height?)​

These return min/max size of a window (or undefined if not set):

window_get_min_width(window)​
window_get_min_height(window)​
window_get_max_width(window)​
window_get_max_height(window)​
Properties
Properties
winwin_set_caption(window, caption)​

Changes the caption/title of a window, like window_set_caption.

Returns whether successful.

winwin_get_caption(window)​

Returns the caption/title of a window.


winwin_set_visible(window, visible)​

Shows/hides a window.

Returns whether successful.

winwin_get_visible(window)​

Returns whether a window is visible.


winwin_set_owner(window, owner)

Changes the window owner (GWLP_HWNDPARENT).

Linked windows always appear over the owner window, minimize along with it, and are generally what you want for any pop-out panels and alike.

owner can be undefined to un-link.

winwin_get_owner(window)​

Returns the current window owner (or undefined if not linked).


winwin_set_taskbar_button_visible(window, show_button)​

Changes whether the taskbar button is visible.

Currently only works for borderless windows (see winwin_config.taskbar_button).

Returns whether successful.

winwin_get_taskbar_button_visible(window)​

Returns whether a window's taskbar button is visible.


winwin_set_clickthrough(window, enable_clickthrough)​

Changes whether a window is click-through.

Click-through windows let through all mouse events - good for overlays or making some portions of the window (e.g. ones with per-pixel transparency) non-interactive.

Returns whether successful.

winwin_get_clickthrough(window)​

Returns whether a window is click-through.


winwin_set_noactivate(window, disable_activation)​

Returns whether the window should have activation disabled.

This prevents giving it focus in most usual means.

Returns whether successful.

winwin_get_noactivate(window)​

Returns whether a window has activation disabled.


winwin_set_close_button(window, state)​

Changes a window's close button state. Allowed values are:

  • 0 (or false): close button is visibly disabled
  • 1 (or true): close button is enabled;
    Closing the window destroys it, which you can check for using winwin_exists.
  • 2: close button is enabled;
    Closing the window hides it (as with winwin_set_visible), which you can check for using winwin_get_visible.

Returns whether successful.

winwin_get_close_button(window)​

Returns a window's close button state.


winwin_set_vsync(window, vsync)​

Changes whether drawing to a specific window uses vertical synchronization.

Using values larger than 1 will draw every N-th blank.

The intricacies of using vsync across multiple windows of the same application have not been extensively studied.

winwin_get_vsync(window)​

Returns the window's vertical synchronization value.

Z-Order
Z-Order
winwin_order_after(window, other_window)​

Puts the window in front of another window.

Returns whether successful.

winwin_order_front(window)​

Moves the window to the front (like when giving it focus).

Returns whether successful.

winwin_order_back(window)​

Moves the window to the back.

Returns whether successful.


winwin_set_topmost(window, enable)​

Changes whether the window should be stay-on-top.

Returns whether successful.

winwin_get_topmost(window)​

Returns whether the window is stay-on-top.

Focus
Focus
winwin_has_focus(window)​

Like window_has_focus, but for additional windows.

winwin_get_focus()​

Returns the window that currently has keyboard focus, or undefined if there's none or it's not part of the current application.

hscript error!
{ fileName => hscript, lineNumber => 9 } hscript:9: Invalid number of parameters. Got 1, required 2 for function 'msdn'
Called from hscript/Interp.hx line 415
Called from a C function
Called from hscript/Interp.hx line 684
Called from hscript/Interp.hx line 381
Called from hscript/Interp.hx line 259
Called from hscript/Interp.hx line 258
Called from hscript/Interp.hx line 254
Called from dmd/tags/TagExec.hx line 76
winwin_set_focus(window)​

Forces keyboard focus upon a window.

Returns whether successful.

Window shapes
Window shapes

Consider these a trimmed-down version of window_shape.

winwin_get_alpha(window)​

Returns opacity of a window in 0..1 range.

winwin_set_alpha(window, alpha)​

Changes opacity of a window (alpha being a 0..1 range).

Returns whether successful.


winwin_set_chromakey(window, color)​

Changes the chromakey color for a window.

Pixels that fully match this color will be see-through and click-through.

Pass -1 instead of the color to disable chromakey.

winwin_get_chromakey(window)​int

Returns the window's chromakey color (-1 if disabled).


winwin_enable_per_pixel_alpha(window)​
winwin_enable_per_pixel_alpha(window)

Quoting my own notes from window_shape,

This function calls DwmEnableBlurBehindWindow for a zero-sized region.

Doing so also happens to enable partial transparency support for the contents of the window ("The alpha values in the window are honored").

For example, if you did

draw_clear_alpha(c_black, 0);

in a Draw event of the only instance in a room, your window would look like an empty frame.

For a proper setup, make sure that "Clear Display Buffer", "Enable Viewports" (with at least one view set up), and "Clear Viewport Background" are enabled for the room; see desktop_friend project for a slightly more sophisticated example.

Now let's talk about the conditions and caveats:

Premultiplied alpha

Windows expects the window pixels to have premultiplied alpha.

In short, instead of just color, the pixels should be colored with

merge_color(c_black, color, alpha)

For sprites, all recent GameMaker versions have a "Premultiply alpha" checkbox in the Texture Settings;

For text and primitives, you'll need to use the above formula;

For mixing multiple semi-transparent images together, see the classic GameMaker surface problems and solutions.

If you don't use pre-multiplied alpha, your window will still work, but the semi-transparent bright portions will appear lighter than they should be.

Hit testing

According to Windows, just because a pixel is now almost or completely transparent, that doesn't mean that you can't click it!

You can use the extension's other functions (be it shapes or chromakey) to narrow down the window's shape, or use the winwin_set_clickthrough function to make the window clickable/un-clickable based on where the cursor is.

One-way ticket

The function has been around since Windows 7, yet it doesn't seem like you can go back to an opaque window once you have called it - even if you pass dwFlags of 0.

You can "top-up" the opacity of the window contents by drawing a black rectangle with bm_add blend mode or any rectangle with gpu_set_colourwriteenable (or draw_set_colour_write_enable for GMS1) of (0, 0, 0, 1).

Cursors
Cursors
winwin_set_cursor(window, cursor)​

Changes the window's current cursor, using GameMaker cr_ constants.

You can also use -18 (cr_no from GM8) for a 🚫 cursor.

Returns whether successful.

winwin_get_cursor(window)​

Returns which cursor (out of the built-in set) the window is currently using.

If it's not using any of those, returns undefined.

Custom cursors:

winwin_set_cursor_handle(window, hcursor)​

Changes the window's current cursor to a new HCURSOR (ptr).

You can only get these from other extensions.

winwin_get_cursor_handle(window)​

Returns the window's current cursor as HCURSOR.

Make sure to not accidentally delete winwin's cursors as these are shared between all windows.

Working with other extensions
Other functions
Known issues
  1. If the user has set up a custom Max Frame Rate override in NVIDIA Control Panel, the limit works as (value / active window count) instead.

    The underlying cause seems to be that NVIDIA driver counts Present calls on per-process basis rather than per-window.

    I haven't come up with a Cool Workaround for this yet, but if the user has set up it themselves, they can also remove the override.

  2. Drawing with shaders into additional windows works weird.

    Probably changes some D3D11 state that I'm not aware of.

    For now, you can draw things with shaders onto a surface and then draw that surface into a window.