Compare commits

...

53 Commits

Author SHA1 Message Date
Jan Schürkamp
e586bdb522
Merge 86fe58df39 into dbadda2683 2025-12-10 09:50:18 -03:00
Camilla Löwy
dbadda2683 Formatting 2025-12-05 13:33:10 +01:00
Camilla Löwy
08449b7183 Linux: Add missing header for ioctl
Fixes #2778
2025-12-04 20:30:12 +01:00
Camilla Löwy
1ce855b0b1 Wayland: Fix missing checks for optional protocol 2025-12-04 20:30:12 +01:00
Camilla Löwy
ebff6606ee Simplify test for shared library build 2025-11-17 22:57:23 +01:00
GamesTrap
86fe58df39
Removed check for Windows 7 or newer in _glfwSetWindowProgressIndicatorWin32() 2025-09-23 19:40:26 +02:00
GamesTrap
d915058373
Fix crash on big-endian systems when calling _glfwAppendDictDataDBusPOSIX()
See https://github.com/libsdl-org/SDL/pull/14029 for reference
2025-09-23 19:36:06 +02:00
GamesTrap
7d23b3c6c1
Merge branch 'glfw:master' into taskbar-progress 2025-09-23 19:11:16 +02:00
GamesTrap
940b7a8668
Merge branch 'glfw:master' into taskbar-progress 2024-06-12 19:57:51 +02:00
Jan Schürkamp
f6722ab492
Merge branch 'glfw:master' into taskbar-progress 2023-12-20 00:19:32 +01:00
Jan Schürkamp
439546caae
Merge branch 'glfw:master' into taskbar-progress 2023-12-08 20:54:19 +01:00
Jan Schürkamp
125b1a2506
Merge branch 'master' into taskbar-progress 2023-11-22 09:38:27 +01:00
Jan Schürkamp
a8e3e5dbe6
Merge branch 'glfw:master' into taskbar-progress 2023-11-18 11:54:13 +01:00
Jan Schürkamp
e597552e75
Merge branch 'glfw:master' into taskbar-progress 2023-08-11 19:58:59 +02:00
GamesTrap
8f1ddbb908
Fixed DBus crash because of illegal characters in executable name 2023-04-12 00:54:47 +02:00
GamesTrap
5842644e2e
Improved DBus repetition 2023-04-12 00:10:39 +02:00
GamesTrap
59514c4bb5
DBus made bus and signal paths unique 2023-04-12 00:02:35 +02:00
GamesTrap
1824da004f
Added caching of desktop file path 2023-04-10 14:16:08 +02:00
GamesTrap
dacb814e8b
Merge upstream/master into origin/taskbar-progress 2023-04-05 19:57:47 +02:00
GamesTrap
589e6b78d9
Added DBus pointer checks 2023-03-08 14:47:40 +01:00
Andreas
ef4d722b76
Merge(#3): Finalized progress API 2023-03-08 14:10:00 +01:00
GamesTrap
9f17a69a67
glfwSetWindowTaskbarProgress Polishing 2023-03-03 16:18:16 +01:00
Jan Schürkamp
d173bf1fed
Merge(#1): Add Cocoa Dock icon progress bar implemenetation 2023-03-03 16:07:15 +01:00
Andreas
14333117c4
Update window.dox
Fix typo in window.dox
2023-03-03 16:04:18 +01:00
Andreas
df8349b747
Delete remaining unintentionally added build files 2023-03-03 15:49:26 +01:00
ws909
8618927ef9 Delete unintentionally added build files 2023-03-03 15:48:33 +01:00
ws909
5301a924c4 Enclose [indicator setControlSize] in availability check 2023-03-03 15:20:11 +01:00
ws909
2244051453 Per-window progress indicator states and values for the MacOS Dock icon 2023-03-02 18:43:27 +01:00
ws909
a9b36d48d7 Simplify _glfwSetWindowTaskbarProgressCocoa
Fix merge issues
Rename taskbarState to progressState
Remove const qualifier from progressState parameter
2023-03-02 15:51:10 +01:00
ws909
0173252ee1 Merge remote-tracking branch 'refs/remotes/GamesTrap/taskbar-progress'
Conflicts:
	src/cocoa_window.m
	tests/window.c
2023-03-02 14:08:25 +01:00
GamesTrap
0f142e3a1a
glfwSetWindowTaskbarProgress Changed value type from int to double 2023-03-02 13:55:42 +01:00
ws909
78e4f9bd67 Make progress bar slider in window test program responsive 2023-03-02 05:20:32 +01:00
ws909
34ee36c088 Initial shared overridable Dock progress bar for all windows in Cocoa 2023-03-02 05:20:05 +01:00
GamesTrap
e387ef3fdf
Added changelog entry to readme 2023-03-01 22:28:48 +01:00
GamesTrap
a480de9b1d
glfwSetWindowTaskbarProgress Renamed parameter completed to value 2023-03-01 22:27:52 +01:00
GamesTrap
3e7b016a00
Renamed GLFW_TASKBAR_PROGRESS_NOPROGRESS to GLFW_TASKBAR_PROGRESS_DISABLED 2023-03-01 22:22:01 +01:00
GamesTrap
1071cf1950
win32_platform.h Removed ShObjIdl_core.h include dependency 2023-03-01 17:25:58 +01:00
GamesTrap
4a66bf3b60
Added documentation to window.dox 2023-02-05 04:19:15 +01:00
GamesTrap
74dac5cfb4
Win32: glfwSetWindowTaskbarProgress now emits GLFW_FEATURE_UNAVAILABLE on Windows Vista and older 2023-02-05 03:03:54 +01:00
GamesTrap
f590075121
Removed taskbar-progress from branch local GH Actions workflow 2023-02-05 02:53:47 +01:00
GamesTrap
19f9247a68
Added documentation 2023-02-05 02:50:53 +01:00
GamesTrap
c8fd71c7e7
Added credit 2023-02-03 23:38:45 +01:00
GamesTrap
fb6826c934
Fixed Cocoa linker error 2023-02-03 23:35:29 +01:00
GamesTrap
efac372213
Fixed Wayland linker error 2023-02-03 23:31:02 +01:00
GamesTrap
8b1b11c3c4
Fixed GitHub Action errors 2023-02-03 23:27:45 +01:00
GamesTrap
48818fc7a9
Unified X11/Wayland code path using _glfwUpdateTaskbarProgressDBusPOSIX() 2023-02-03 23:23:25 +01:00
GamesTrap
7d5bb13b88
Enabled GitHub Action for the taskbar-progress branch 2023-02-03 22:48:47 +01:00
Jan Schürkamp
48e08616d9
Merge branch 'glfw:master' into taskbar-progress 2023-02-01 00:28:58 +01:00
GamesTrap
11ddcdde9f
Merge remote-tracking branch 'upstream/master' into taskbar-progress 2022-10-14 18:05:48 +02:00
Jan Schürkamp
ec524797cc
Merge branch 'glfw:master' into taskbar-progress 2022-09-02 03:15:13 +02:00
GamesTrap
827cf79f63
Linux Added support for taskbar progress (KDE & Unity) 2022-09-02 02:49:53 +02:00
Jan Schürkamp
fb4c22ed9c
Merge branch 'glfw:master' into taskbar-progress 2022-08-06 16:38:09 +02:00
GamesTrap
8acb72b9a1
Win32 Added support for taskbar progress 2022-07-23 21:28:35 +02:00
29 changed files with 1258 additions and 19 deletions

View File

@ -38,11 +38,7 @@ set(GLFW_LIBRARY_TYPE "${GLFW_LIBRARY_TYPE}" CACHE STRING
"Library type override for GLFW (SHARED, STATIC, OBJECT, or empty to follow BUILD_SHARED_LIBS)") "Library type override for GLFW (SHARED, STATIC, OBJECT, or empty to follow BUILD_SHARED_LIBS)")
if (GLFW_LIBRARY_TYPE) if (GLFW_LIBRARY_TYPE)
if (GLFW_LIBRARY_TYPE STREQUAL "SHARED") string(COMPARE EQUAL "${GLFW_LIBRARY_TYPE}" "SHARED" GLFW_BUILD_SHARED_LIBRARY)
set(GLFW_BUILD_SHARED_LIBRARY TRUE)
else()
set(GLFW_BUILD_SHARED_LIBRARY FALSE)
endif()
else() else()
set(GLFW_BUILD_SHARED_LIBRARY ${BUILD_SHARED_LIBS}) set(GLFW_BUILD_SHARED_LIBRARY ${BUILD_SHARED_LIBS})
endif() endif()

View File

@ -113,6 +113,7 @@ video tutorials.
- IntellectualKitty - IntellectualKitty
- Aaron Jacobs - Aaron Jacobs
- JannikGM - JannikGM
- Andreas O. Jansen
- Erik S. V. Jansson - Erik S. V. Jansson
- jjYBdx4IL - jjYBdx4IL
- Peter Johnson - Peter Johnson
@ -162,6 +163,7 @@ video tutorials.
- Marcel Metz - Marcel Metz
- Liam Middlebrook - Liam Middlebrook
- mightgoyardstill - mightgoyardstill
- Mihail
- Ave Milia - Ave Milia
- Icyllis Milica - Icyllis Milica
- Jonathan Miller - Jonathan Miller
@ -232,6 +234,7 @@ video tutorials.
- Yoshinori Sano - Yoshinori Sano
- Brandon Schaefer - Brandon Schaefer
- Sebastian Schuberth - Sebastian Schuberth
- Jan Schuerkamp
- Scr3amer - Scr3amer
- Jan Schürkamp - Jan Schürkamp
- Christian Sdunek - Christian Sdunek

View File

@ -120,6 +120,7 @@ information on what to include when reporting a bug.
## Changelog since 3.4 ## Changelog since 3.4
- Added `glfwSetWindowProgressIndicator` for displaying progress on the dock or taskbar (#2286,#1183)
- Added `GLFW_UNLIMITED_MOUSE_BUTTONS` input mode that allows mouse buttons beyond - Added `GLFW_UNLIMITED_MOUSE_BUTTONS` input mode that allows mouse buttons beyond
the limit of the mouse button tokens to be reported (#2423) the limit of the mouse button tokens to be reported (#2423)
- Added `glfwGetEGLConfig` function to query the `EGLConfig` of a window (#2045) - Added `glfwGetEGLConfig` function to query the `EGLConfig` of a window (#2045)
@ -146,11 +147,14 @@ information on what to include when reporting a bug.
from a modal to the content area from a modal to the content area
- [Wayland] Bugfix: free modules at end of terminate function to resolve - [Wayland] Bugfix: free modules at end of terminate function to resolve
potential segmentation fault (#2744) potential segmentation fault (#2744)
- [Wayland] Bugfix: Confining or disabling the cursor could segfault on
compositors without `pointer-constraints-unstable-v1`
- [X11] Bugfix: Running without a WM could trigger an assert (#2593,#2601,#2631) - [X11] Bugfix: Running without a WM could trigger an assert (#2593,#2601,#2631)
- [X11] Bugfix: Occasional crash when an idle display awakes (#2766) - [X11] Bugfix: Occasional crash when an idle display awakes (#2766)
- [X11] Bugfix: Prevent BadWindow when creating small windows with a content scale - [X11] Bugfix: Prevent BadWindow when creating small windows with a content scale
less than 1 (#2754) less than 1 (#2754)
- [X11] Bugfix: Clamp width and height to >= 1 to prevent BadValue error and app exit - [X11] Bugfix: Clamp width and height to >= 1 to prevent BadValue error and app exit
- [Linux] Bugfix: The header for `ioctl` was only implicitly included (#2778)
- [Null] Added Vulkan 'window' surface creation via `VK_EXT_headless_surface` - [Null] Added Vulkan 'window' surface creation via `VK_EXT_headless_surface`
- [Null] Added EGL context creation on Mesa via `EGL_MESA_platform_surfaceless` - [Null] Added EGL context creation on Mesa via `EGL_MESA_platform_surfaceless`
- [EGL] Allowed native access on Wayland with `GLFW_CONTEXT_CREATION_API` set to - [EGL] Allowed native access on Wayland with `GLFW_CONTEXT_CREATION_API` set to

View File

@ -1214,6 +1214,26 @@ not supported, the application as a whole. Once the user has given it
attention, the system will automatically end the request. attention, the system will automatically end the request.
### Window progress indicator {#window_progress_indicator}
If you wish to display the progress of some action on the Dock icon or task bar, you can
do this with @ref glfwSetWindowProgressIndicator.
```c
glfwSetWindowProgressIndicator(window, GLFW_PROGRESS_INDICATOR_NORMAL, 0.5);
```
There are different progress states available for you to use:
- @ref GLFW_PROGRESS_INDICATOR_DISABLED
- @ref GLFW_PROGRESS_INDICATOR_INDETERMINATE
- @ref GLFW_PROGRESS_INDICATOR_NORMAL
- @ref GLFW_PROGRESS_INDICATOR_ERROR
- @ref GLFW_PROGRESS_INDICATOR_PAUSED
The last argument is the progress percentage to display.
It has a valid range of 0.0 to 1.0.
### Window damage and refresh {#window_refresh} ### Window damage and refresh {#window_refresh}
If you wish to be notified when the contents of a window is damaged and needs If you wish to be notified when the contents of a window is damaged and needs

View File

@ -1288,6 +1288,59 @@ extern "C" {
#define GLFW_HAND_CURSOR GLFW_POINTING_HAND_CURSOR #define GLFW_HAND_CURSOR GLFW_POINTING_HAND_CURSOR
/*! @} */ /*! @} */
/*! @addtogroup window
* @{ */
/*! @brief Disable the progress bar.
*
* Disable the progress bar.
*
* Used by @ref window_progress_indicator.
*/
#define GLFW_PROGRESS_INDICATOR_DISABLED 0
/*! @brief Display the progress bar in an indeterminate state.
*
* Display the progress bar in an indeterminate state.
*
* @remark @win32 This displays the progress bar animation cycling repeatedly.
*
* @remark @x11 @wayland This behaves like @ref GLFW_PROGRESS_INDICATOR_NORMAL.
*
* @remark @macos This displays a standard indeterminate `NSProgressIndicator`.
*
* Used by @ref window_progress_indicator.
*/
#define GLFW_PROGRESS_INDICATOR_INDETERMINATE 1
/*! @brief Display the normal progress bar.
*
* Display the normal progress bar.
*
* Used by @ref window_progress_indicator.
*/
#define GLFW_PROGRESS_INDICATOR_NORMAL 2
/*! @brief Display the progress bar in an error state.
*
* Display the progress bar in an error state.
*
* @remark @win32 This displays a red progress bar.
*
* @remark @x11 @wayland @macos This behaves like @ref GLFW_PROGRESS_INDICATOR_NORMAL.
*
* Used by @ref window_progress_indicator.
*/
#define GLFW_PROGRESS_INDICATOR_ERROR 3
/*! @brief Display the progress bar in a paused state.
*
* Display the progress bar in a paused state.
*
* @remark @win32 This displays a yellow progress bar.
*
* @remark @x11 @wayland @macos This behaves like @ref GLFW_PROGRESS_INDICATOR_NORMAL.
*
* Used by @ref window_progress_indicator.
*/
#define GLFW_PROGRESS_INDICATOR_PAUSED 4
/*! @} */
#define GLFW_CONNECTED 0x00040001 #define GLFW_CONNECTED 0x00040001
#define GLFW_DISCONNECTED 0x00040002 #define GLFW_DISCONNECTED 0x00040002
@ -3413,6 +3466,46 @@ GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title);
*/ */
GLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* images); GLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* images);
/*! @brief Sets the dock or taskbar progress indicator for the specified window.
*
* This function sets the dock or taskbar progress indicator of the specified window.
*
* @param[in] window The window whose progress to set.
* @param[in] progressState The state of the progress to be displayed in the dock
* or taskbar. Valid values are: @ref GLFW_PROGRESS_INDICATOR_DISABLED,
* @ref GLFW_PROGRESS_INDICATOR_INDETERMINATE, @ref GLFW_PROGRESS_INDICATOR_NORMAL,
* @ref GLFW_PROGRESS_INDICATOR_ERROR and @ref GLFW_PROGRESS_INDICATOR_PAUSED.
* @param[in] value The amount of completed progress to set. Valid range is 0.0 to 1.0.
* This is ignored if progressState is set to @ref GLFW_PROGRESS_INDICATOR_DISABLED.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
* GLFW_INVALID_VALUE, @ref GLFW_INVALID_ENUM, @ref GLFW_PLATFORM_ERROR,
* @ref GLFW_FEATURE_UNIMPLEMENTED and @ref GLFW_FEATURE_UNAVAILABLE (see remarks).
*
* @remark @win32 On Windows Vista and earlier, this function will emit
* @ref GLFW_FEATURE_UNAVAILABLE.
*
* @remark @macos There exists only one Dock icon progress bar, and this
* displays the combined values of all the windows.
*
* @remark @x11 @wayland Requires a valid application desktop file with the same name
* as the compiled executable. Due to limitations in the Unity Launcher API
* @ref GLFW_PROGRESS_INDICATOR_INDETERMINATE, @ref GLFW_PROGRESS_INDICATOR_ERROR
* and @ref GLFW_PROGRESS_INDICATOR_PAUSED have the same behaviour as
* @ref GLFW_PROGRESS_INDICATOR_NORMAL. The Unity Launcher API is only known
* to be supported on the Unity and KDE desktop environments; on other desktop
* environments this function may do nothing.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref window_progress_indicator
*
* @since Added in version 3.4.
*
* @ingroup window
*/
GLFWAPI void glfwSetWindowProgressIndicator(GLFWwindow* window, int progressState, double value);
/*! @brief Retrieves the position of the content area of the specified window. /*! @brief Retrieves the position of the content area of the specified window.
* *
* This function retrieves the position, in screen coordinates, of the * This function retrieves the position, in screen coordinates, of the

View File

@ -17,7 +17,8 @@ elseif (WIN32)
win32_time.c win32_thread.c) win32_time.c win32_thread.c)
else() else()
target_sources(glfw PRIVATE posix_time.h posix_thread.h posix_module.c target_sources(glfw PRIVATE posix_time.h posix_thread.h posix_module.c
posix_time.c posix_thread.c) posix_time.c posix_thread.c posix_dbus.h
posix_dbus.c)
endif() endif()
add_custom_target(update_mappings add_custom_target(update_mappings

View File

@ -526,6 +526,7 @@ GLFWbool _glfwConnectCocoa(int platformID, _GLFWplatform* platform)
.destroyWindow = _glfwDestroyWindowCocoa, .destroyWindow = _glfwDestroyWindowCocoa,
.setWindowTitle = _glfwSetWindowTitleCocoa, .setWindowTitle = _glfwSetWindowTitleCocoa,
.setWindowIcon = _glfwSetWindowIconCocoa, .setWindowIcon = _glfwSetWindowIconCocoa,
.setWindowProgressIndicator = _glfwSetWindowProgressIndicatorCocoa,
.getWindowPos = _glfwGetWindowPosCocoa, .getWindowPos = _glfwGetWindowPosCocoa,
.setWindowPos = _glfwSetWindowPosCocoa, .setWindowPos = _glfwSetWindowPosCocoa,
.getWindowSize = _glfwGetWindowSizeCocoa, .getWindowSize = _glfwGetWindowSizeCocoa,
@ -646,6 +647,12 @@ int _glfwInitCocoa(void)
void _glfwTerminateCocoa(void) void _glfwTerminateCocoa(void)
{ {
@autoreleasepool { @autoreleasepool {
if (_glfw.ns.dockProgressIndicator.view != nil)
{
[_glfw.ns.dockProgressIndicator.view removeFromSuperview];
[_glfw.ns.dockProgressIndicator.view release];
}
if (_glfw.ns.inputSource) if (_glfw.ns.inputSource)
{ {

View File

@ -156,6 +156,11 @@ typedef struct _GLFWwindowNS
// since the last cursor motion event was processed // since the last cursor motion event was processed
// This is kept to counteract Cocoa doing the same internally // This is kept to counteract Cocoa doing the same internally
double cursorWarpDeltaX, cursorWarpDeltaY; double cursorWarpDeltaX, cursorWarpDeltaY;
struct {
int state;
double value;
} dockProgressIndicator;
} _GLFWwindowNS; } _GLFWwindowNS;
// Cocoa-specific global data // Cocoa-specific global data
@ -189,6 +194,13 @@ typedef struct _GLFWlibraryNS
PFN_LMGetKbdType GetKbdType; PFN_LMGetKbdType GetKbdType;
CFStringRef kPropertyUnicodeKeyLayoutData; CFStringRef kPropertyUnicodeKeyLayoutData;
} tis; } tis;
struct {
id view;
int windowCount;
int indeterminateCount;
double totalValue;
} dockProgressIndicator;
} _GLFWlibraryNS; } _GLFWlibraryNS;
// Cocoa-specific per-monitor data // Cocoa-specific per-monitor data
@ -218,6 +230,7 @@ GLFWbool _glfwCreateWindowCocoa(_GLFWwindow* window, const _GLFWwndconfig* wndco
void _glfwDestroyWindowCocoa(_GLFWwindow* window); void _glfwDestroyWindowCocoa(_GLFWwindow* window);
void _glfwSetWindowTitleCocoa(_GLFWwindow* window, const char* title); void _glfwSetWindowTitleCocoa(_GLFWwindow* window, const char* title);
void _glfwSetWindowIconCocoa(_GLFWwindow* window, int count, const GLFWimage* images); void _glfwSetWindowIconCocoa(_GLFWwindow* window, int count, const GLFWimage* images);
void _glfwSetWindowProgressIndicatorCocoa(_GLFWwindow* window, int progressState, double value);
void _glfwGetWindowPosCocoa(_GLFWwindow* window, int* xpos, int* ypos); void _glfwGetWindowPosCocoa(_GLFWwindow* window, int* xpos, int* ypos);
void _glfwSetWindowPosCocoa(_GLFWwindow* window, int xpos, int ypos); void _glfwSetWindowPosCocoa(_GLFWwindow* window, int xpos, int ypos);
void _glfwGetWindowSizeCocoa(_GLFWwindow* window, int* width, int* height); void _glfwGetWindowSizeCocoa(_GLFWwindow* window, int* width, int* height);

View File

@ -198,6 +198,83 @@ static NSUInteger translateKeyToModifierFlag(int key)
// //
static const NSRange kEmptyRange = { NSNotFound, 0 }; static const NSRange kEmptyRange = { NSNotFound, 0 };
static NSProgressIndicator* createProgressIndicator(const NSDockTile* dockTile)
{
NSView* contentView = [dockTile contentView];
NSProgressIndicator* indicator = [[NSProgressIndicator alloc] initWithFrame:NSMakeRect(0.0f, 0.0f, contentView.frame.size.width, 15.0f)];
[indicator setStyle:NSProgressIndicatorStyleBar];
if (@available(macOS 11.0, *))
{
[indicator setControlSize:NSControlSizeLarge];
}
[indicator setMinValue:0.0f];
[indicator setMaxValue:1.0f];
[indicator sizeToFit];
[contentView addSubview:indicator];
_glfw.ns.dockProgressIndicator.view = indicator;
return indicator;
}
static void setDockProgressIndicator(int progressState, double value)
{
NSProgressIndicator* indicator = _glfw.ns.dockProgressIndicator.view;
NSDockTile* dockTile = [[NSApplication sharedApplication] dockTile];
if (indicator == nil)
{
if ([dockTile contentView] == nil)
{
NSImageView *iconView = [[NSImageView alloc] init];
[iconView setImage:[[NSApplication sharedApplication] applicationIconImage]];
[dockTile setContentView:iconView];
[iconView release];
}
indicator = createProgressIndicator(dockTile);
}
// ### Switching from INDETERMINATE to NORMAL, PAUSED or ERROR requires 2 invocations in different frames.
// In MacOS 12 (and probably other versions), an indeterminate progress bar is rendered as a normal bar
// with 0.0 progress. So when calling [progressIndicator setIndeterminate:YES], the indicator actually
// sets its doubleValue to 0.0.
// The bug is caused by NSProgressIndicator not immediately updating its value when it's increasing.
// This code illustrates the exact same problem, but this time from NORMAL, PAUSED and ERROR to INDETERMINATE:
//
// if (progressState == GLFW_PROGRESS_INDICATOR_INDETERMINATE)
// [progressIndicator setDoubleValue:0.75];
// else
// [progressIndicator setDoubleValue:0.25];
//
// This is likely a bug in Cocoa.
//
// ### Progress increments are delayed
// What this also means, is that each time the progress increments, the bar's progress will be 1 frame delayed,
// and only updated once a higher or similar value is again set the next frame.
// Workaround for the aforementioned issues. If there's any versions of MacOS where
// this issue is not present, this should be ommitted in those versions.
if ([indicator isIndeterminate] || [indicator doubleValue] < value)
{
[indicator removeFromSuperview];
[indicator release];
indicator = createProgressIndicator(dockTile);
}
[indicator setIndeterminate:progressState == GLFW_PROGRESS_INDICATOR_INDETERMINATE];
[indicator setHidden:progressState == GLFW_PROGRESS_INDICATOR_DISABLED];
[indicator setDoubleValue:value];
[dockTile display];
}
//------------------------------------------------------------------------ //------------------------------------------------------------------------
// Delegate for window related notifications // Delegate for window related notifications
@ -990,6 +1067,8 @@ GLFWbool _glfwCreateWindowCocoa(_GLFWwindow* window,
void _glfwDestroyWindowCocoa(_GLFWwindow* window) void _glfwDestroyWindowCocoa(_GLFWwindow* window)
{ {
@autoreleasepool { @autoreleasepool {
_glfwSetWindowProgressIndicatorCocoa(window, GLFW_PROGRESS_INDICATOR_DISABLED, 0.0);
if (_glfw.ns.disabledCursorWindow == window) if (_glfw.ns.disabledCursorWindow == window)
_glfw.ns.disabledCursorWindow = NULL; _glfw.ns.disabledCursorWindow = NULL;
@ -1036,6 +1115,62 @@ void _glfwSetWindowIconCocoa(_GLFWwindow* window,
"Cocoa: Regular windows do not have icons on macOS"); "Cocoa: Regular windows do not have icons on macOS");
} }
void _glfwSetWindowProgressIndicatorCocoa(_GLFWwindow* window, int progressState, double value)
{
if (progressState == GLFW_PROGRESS_INDICATOR_ERROR || progressState == GLFW_PROGRESS_INDICATOR_PAUSED)
progressState = GLFW_PROGRESS_INDICATOR_NORMAL;
const int oldState = window->ns.dockProgressIndicator.state;
const int state = progressState;
const double oldValue = window->ns.dockProgressIndicator.value;
if (oldState == state)
{
if (state == GLFW_PROGRESS_INDICATOR_DISABLED ||
state == GLFW_PROGRESS_INDICATOR_INDETERMINATE ||
oldValue == value)
return;
}
if (oldState != state)
{
// Reset
if (oldState == GLFW_PROGRESS_INDICATOR_INDETERMINATE)
--_glfw.ns.dockProgressIndicator.indeterminateCount;
if (oldState != GLFW_PROGRESS_INDICATOR_DISABLED)
{
--_glfw.ns.dockProgressIndicator.windowCount;
_glfw.ns.dockProgressIndicator.totalValue -= oldValue;
}
// Set
if (state == GLFW_PROGRESS_INDICATOR_INDETERMINATE)
++_glfw.ns.dockProgressIndicator.indeterminateCount;
if (state != GLFW_PROGRESS_INDICATOR_DISABLED)
{
++_glfw.ns.dockProgressIndicator.windowCount;
_glfw.ns.dockProgressIndicator.totalValue += value;
}
}
else if (state != GLFW_PROGRESS_INDICATOR_DISABLED)
_glfw.ns.dockProgressIndicator.totalValue += (value - oldValue);
if (_glfw.ns.dockProgressIndicator.windowCount > _glfw.ns.dockProgressIndicator.indeterminateCount)
{
const double finalValue = _glfw.ns.dockProgressIndicator.totalValue / _glfw.ns.dockProgressIndicator.windowCount;
setDockProgressIndicator(GLFW_PROGRESS_INDICATOR_NORMAL, finalValue);
}
else if (_glfw.ns.dockProgressIndicator.indeterminateCount > 0)
setDockProgressIndicator(GLFW_PROGRESS_INDICATOR_INDETERMINATE, 0.0f);
else
setDockProgressIndicator(GLFW_PROGRESS_INDICATOR_DISABLED, 0.0f);
window->ns.dockProgressIndicator.state = state;
window->ns.dockProgressIndicator.value = value;
}
void _glfwGetWindowPosCocoa(_GLFWwindow* window, int* xpos, int* ypos) void _glfwGetWindowPosCocoa(_GLFWwindow* window, int* xpos, int* ypos)
{ {
@autoreleasepool { @autoreleasepool {

View File

@ -717,6 +717,7 @@ struct _GLFWplatform
void (*destroyWindow)(_GLFWwindow*); void (*destroyWindow)(_GLFWwindow*);
void (*setWindowTitle)(_GLFWwindow*,const char*); void (*setWindowTitle)(_GLFWwindow*,const char*);
void (*setWindowIcon)(_GLFWwindow*,int,const GLFWimage*); void (*setWindowIcon)(_GLFWwindow*,int,const GLFWimage*);
void (*setWindowProgressIndicator)(_GLFWwindow*,const int,double);
void (*getWindowPos)(_GLFWwindow*,int*,int*); void (*getWindowPos)(_GLFWwindow*,int*,int*);
void (*setWindowPos)(_GLFWwindow*,int,int); void (*setWindowPos)(_GLFWwindow*,int,int);
void (*getWindowSize)(_GLFWwindow*,int*,int*); void (*getWindowSize)(_GLFWwindow*,int*,int*);
@ -883,6 +884,7 @@ struct _GLFWlibrary
GLFW_PLATFORM_LIBRARY_WINDOW_STATE GLFW_PLATFORM_LIBRARY_WINDOW_STATE
GLFW_PLATFORM_LIBRARY_CONTEXT_STATE GLFW_PLATFORM_LIBRARY_CONTEXT_STATE
GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE
GLFW_PLATFORM_LIBRARY_DBUS_STATE
}; };
// Global state shared between compilation units of GLFW // Global state shared between compilation units of GLFW

View File

@ -32,6 +32,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/inotify.h> #include <sys/inotify.h>
#include <sys/ioctl.h>
#include <fcntl.h> #include <fcntl.h>
#include <errno.h> #include <errno.h>
#include <dirent.h> #include <dirent.h>

View File

@ -72,6 +72,7 @@ GLFWbool _glfwConnectNull(int platformID, _GLFWplatform* platform)
.destroyWindow = _glfwDestroyWindowNull, .destroyWindow = _glfwDestroyWindowNull,
.setWindowTitle = _glfwSetWindowTitleNull, .setWindowTitle = _glfwSetWindowTitleNull,
.setWindowIcon = _glfwSetWindowIconNull, .setWindowIcon = _glfwSetWindowIconNull,
.setWindowProgressIndicator = _glfwSetWindowProgressIndicatorNull,
.getWindowPos = _glfwGetWindowPosNull, .getWindowPos = _glfwGetWindowPosNull,
.setWindowPos = _glfwSetWindowPosNull, .setWindowPos = _glfwSetWindowPosNull,
.getWindowSize = _glfwGetWindowSizeNull, .getWindowSize = _glfwGetWindowSizeNull,

View File

@ -223,6 +223,7 @@ GLFWbool _glfwCreateWindowNull(_GLFWwindow* window, const _GLFWwndconfig* wndcon
void _glfwDestroyWindowNull(_GLFWwindow* window); void _glfwDestroyWindowNull(_GLFWwindow* window);
void _glfwSetWindowTitleNull(_GLFWwindow* window, const char* title); void _glfwSetWindowTitleNull(_GLFWwindow* window, const char* title);
void _glfwSetWindowIconNull(_GLFWwindow* window, int count, const GLFWimage* images); void _glfwSetWindowIconNull(_GLFWwindow* window, int count, const GLFWimage* images);
void _glfwSetWindowProgressIndicatorNull(_GLFWwindow* window, int progressState, double value);
void _glfwSetWindowMonitorNull(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate); void _glfwSetWindowMonitorNull(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate);
void _glfwGetWindowPosNull(_GLFWwindow* window, int* xpos, int* ypos); void _glfwGetWindowPosNull(_GLFWwindow* window, int* xpos, int* ypos);
void _glfwSetWindowPosNull(_GLFWwindow* window, int xpos, int ypos); void _glfwSetWindowPosNull(_GLFWwindow* window, int xpos, int ypos);

View File

@ -186,6 +186,10 @@ void _glfwSetWindowIconNull(_GLFWwindow* window, int count, const GLFWimage* ima
{ {
} }
void _glfwSetWindowProgressIndicatorNull(_GLFWwindow* window, int progressState, double value)
{
}
void _glfwSetWindowMonitorNull(_GLFWwindow* window, void _glfwSetWindowMonitorNull(_GLFWwindow* window,
_GLFWmonitor* monitor, _GLFWmonitor* monitor,
int xpos, int ypos, int xpos, int ypos,

View File

@ -187,6 +187,7 @@
#define GLFW_BUILD_COCOA_TIMER #define GLFW_BUILD_COCOA_TIMER
#else #else
#define GLFW_BUILD_POSIX_TIMER #define GLFW_BUILD_POSIX_TIMER
#define GLFW_BUILD_POSIX_DBUS
#endif #endif
#if defined(GLFW_BUILD_WIN32_TIMER) #if defined(GLFW_BUILD_WIN32_TIMER)
@ -200,6 +201,13 @@
#define GLFW_PLATFORM_LIBRARY_TIMER_STATE GLFW_POSIX_LIBRARY_TIMER_STATE #define GLFW_PLATFORM_LIBRARY_TIMER_STATE GLFW_POSIX_LIBRARY_TIMER_STATE
#endif #endif
#if defined(GLFW_BUILD_POSIX_DBUS)
#include "posix_dbus.h"
#define GLFW_PLATFORM_LIBRARY_DBUS_STATE GLFW_POSIX_LIBRARY_DBUS_STATE
#else
#define GLFW_PLATFORM_LIBRARY_DBUS_STATE
#endif
#if defined(_WIN32) #if defined(_WIN32)
#define GLFW_BUILD_WIN32_MODULE #define GLFW_BUILD_WIN32_MODULE
#else #else

519
src/posix_dbus.c Normal file
View File

@ -0,0 +1,519 @@
//========================================================================
// GLFW 3.4 POSIX - www.glfw.org
//------------------------------------------------------------------------
// Copyright (c) 2023 Camilla Löwy <elmindreda@glfw.org>
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
//
//========================================================================
// It is fine to use C99 in this file because it will not be built with VS
//========================================================================
#define _GNU_SOURCE
#include "internal.h"
#include <string.h>
#include <limits.h>
#include <unistd.h>
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
void _glfwInitDBusPOSIX(void)
{
//Initialize DBus library functions
_glfw.dbus.handle = NULL;
_glfw.dbus.connection = NULL;
_glfw.dbus.handle = _glfwPlatformLoadModule("libdbus-1.so.3");
if (!_glfw.dbus.handle)
return;
_glfw.dbus.error_init = (PFN_dbus_error_init)
_glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_error_init");
_glfw.dbus.error_is_set = (PFN_dbus_error_is_set)
_glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_error_is_set");
_glfw.dbus.error_free = (PFN_dbus_error_free)
_glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_error_free");
_glfw.dbus.connection_unref = (PFN_dbus_connection_unref)
_glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_connection_unref");
_glfw.dbus.connection_send = (PFN_dbus_connection_send)
_glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_connection_send");
_glfw.dbus.connection_flush = (PFN_dbus_connection_flush)
_glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_connection_flush");
_glfw.dbus.bus_request_name = (PFN_dbus_bus_request_name)
_glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_bus_request_name");
_glfw.dbus.bus_get = (PFN_dbus_bus_get)
_glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_bus_get");
_glfw.dbus.message_unref = (PFN_dbus_message_unref)
_glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_message_unref");
_glfw.dbus.message_new_signal = (PFN_dbus_message_new_signal)
_glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_message_new_signal");
_glfw.dbus.message_iter_init_append = (PFN_dbus_message_iter_init_append)
_glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_message_iter_init_append");
_glfw.dbus.message_iter_append_basic = (PFN_dbus_message_iter_append_basic)
_glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_message_iter_append_basic");
_glfw.dbus.message_iter_open_container = (PFN_dbus_message_iter_open_container)
_glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_message_iter_open_container");
_glfw.dbus.message_iter_close_container = (PFN_dbus_message_iter_close_container)
_glfwPlatformGetModuleSymbol(_glfw.dbus.handle, "dbus_message_iter_close_container");
if (!_glfw.dbus.error_init ||
!_glfw.dbus.error_is_set ||
!_glfw.dbus.error_free ||
!_glfw.dbus.connection_unref ||
!_glfw.dbus.connection_send ||
!_glfw.dbus.connection_flush ||
!_glfw.dbus.bus_request_name ||
!_glfw.dbus.bus_get ||
!_glfw.dbus.message_unref ||
!_glfw.dbus.message_new_signal ||
!_glfw.dbus.message_iter_init_append ||
!_glfw.dbus.message_iter_append_basic ||
!_glfw.dbus.message_iter_open_container ||
!_glfw.dbus.message_iter_close_container)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"POSIX: Failed to load DBus entry points");
return;
}
//Initialize DBus connection
dbus_error_init(&_glfw.dbus.error);
_glfw.dbus.connection = dbus_bus_get(DBUS_BUS_SESSION, &_glfw.dbus.error);
//Check for errors
if(dbus_error_is_set(&_glfw.dbus.error) || !_glfw.dbus.connection)
{
if(dbus_error_is_set(&_glfw.dbus.error))
dbus_error_free(&_glfw.dbus.error);
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to connect to DBus");
dbus_connection_unref(_glfw.dbus.connection);
_glfw.dbus.connection = NULL;
return;
}
else
{
//Request name
_glfwCacheLegalExecutableNameDBusPOSIX();
if(!_glfw.dbus.legalExecutableName)
return;
//"org.glfw.<exe_name>_<pid>"
char* busName = _glfw_calloc(21 + strlen(_glfw.dbus.legalExecutableName), sizeof(char));
if(!busName)
{
_glfwInputError(GLFW_OUT_OF_MEMORY, "Failed to allocate memory for bus name");
return;
}
memset(busName, '\0', (21 + strlen(_glfw.dbus.legalExecutableName)) * sizeof(char));
const pid_t pid = getpid();
sprintf(busName, "org.glfw.%s_%d", _glfw.dbus.legalExecutableName, pid);
const int res = dbus_bus_request_name(_glfw.dbus.connection, busName, DBUS_NAME_FLAG_REPLACE_EXISTING, &_glfw.dbus.error);
_glfw_free(busName);
//Check for errors
if(dbus_error_is_set(&_glfw.dbus.error) || res != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
{
if(dbus_error_is_set(&_glfw.dbus.error))
dbus_error_free(&_glfw.dbus.error);
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to request DBus name");
dbus_connection_unref(_glfw.dbus.connection);
_glfw.dbus.connection = NULL;
}
}
_glfwCacheFullExecutableNameDBusPOSIX();
_glfwCacheDesktopFilePathDBusPOSIX();
_glfwCacheSignalNameDBusPOSIX();
}
void _glfwCacheSignalNameDBusPOSIX(void)
{
if(!_glfw.dbus.legalExecutableName)
return;
//"/org/glfw/<exe_name>_<pid>"
char* signalName = _glfw_calloc(22 + strlen(_glfw.dbus.legalExecutableName), sizeof(char));
if(!signalName)
{
_glfwInputError(GLFW_OUT_OF_MEMORY, "Failed to allocate memory for signal name");
return;
}
memset(signalName, '\0', (22 + strlen(_glfw.dbus.legalExecutableName)) * sizeof(char));
const pid_t pid = getpid();
if(sprintf(signalName, "/org/glfw/%s_%d", _glfw.dbus.legalExecutableName, pid) < 0)
{
_glfwInputError(GLFW_PLATFORM, "Failed to create signal name");
_glfw_free(signalName);
return;
}
_glfw.dbus.signalName = signalName;
}
void _glfwCacheFullExecutableNameDBusPOSIX(void)
{
char exeName[PATH_MAX];
memset(exeName, 0, sizeof(char) * PATH_MAX);
if(readlink("/proc/self/exe", exeName, PATH_MAX) == -1)
{
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to get name of the running executable");
return;
}
char* exeNameEnd = strchr(exeName, '\0');
char* lastFound = strrchr(exeName, '/');
if(!lastFound || !exeNameEnd)
{
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to get name of the running executable");
return;
}
unsigned int exeNameLength = (exeNameEnd - lastFound) - 1;
char* exeNameFinal = _glfw_calloc(exeNameLength + 1, sizeof(char));
if(!exeNameFinal)
{
_glfwInputError(GLFW_OUT_OF_MEMORY, "Failed to allocate memory for executable name");
return;
}
memset(exeNameFinal, 0, sizeof(char) * (exeNameLength + 1));
memcpy(exeNameFinal, (lastFound + 1), exeNameLength);
_glfw.dbus.fullExecutableName = exeNameFinal;
}
void _glfwCacheLegalExecutableNameDBusPOSIX(void)
{
//The executable name is stripped of any illegal characters
//according to the DBus specification
int i = 0;
int validExeNameLength = 0;
int output = 0;
char exeName[PATH_MAX];
memset(exeName, 0, sizeof(char) * PATH_MAX);
if(readlink("/proc/self/exe", exeName, PATH_MAX) == -1)
{
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to get name of the running executable");
return;
}
char* exeNameEnd = strchr(exeName, '\0');
char* lastFound = strrchr(exeName, '/');
if(!lastFound || !exeNameEnd)
{
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to get name of the running executable");
return;
}
unsigned int exeNameLength = (exeNameEnd - lastFound) - 1;
for(i = 0; i < exeNameLength; ++i)
{
if(isalnum(*(lastFound + 1 + i)))
validExeNameLength++;
}
char* exeNameFinal = _glfw_calloc(validExeNameLength + 1, sizeof(char));
if(!exeNameFinal)
{
_glfwInputError(GLFW_OUT_OF_MEMORY, "Failed to allocate memory for executable name");
return;
}
memset(exeNameFinal, 0, sizeof(char) * (validExeNameLength + 1));
for(i = 0; i < exeNameLength; ++i)
{
if(isalnum(*(lastFound + 1 + i)))
exeNameFinal[output++] = *(lastFound + 1 + i);
}
_glfw.dbus.legalExecutableName = exeNameFinal;
}
void _glfwCacheDesktopFilePathDBusPOSIX(void)
{
if(!_glfw.dbus.fullExecutableName)
return;
//Cache path of .desktop file
//Create our final desktop file uri
//"application://<exe_name>.desktop"
unsigned int desktopFileLength = strlen("application://") + strlen(_glfw.dbus.fullExecutableName) + strlen(".desktop") + 1;
_glfw.dbus.desktopFilePath = _glfw_calloc(desktopFileLength, sizeof(char));
if(!_glfw.dbus.desktopFilePath)
{
_glfwInputError(GLFW_OUT_OF_MEMORY, "Failed to allocate memory for .desktop file path");
return;
}
memset(_glfw.dbus.desktopFilePath, 0, sizeof(char) * desktopFileLength);
strcpy(_glfw.dbus.desktopFilePath, "application://");
memcpy(_glfw.dbus.desktopFilePath + strlen("application://"), _glfw.dbus.fullExecutableName, strlen(_glfw.dbus.fullExecutableName));
strcpy(_glfw.dbus.desktopFilePath + strlen("application://") + strlen(_glfw.dbus.fullExecutableName), ".desktop");
_glfw.dbus.desktopFilePath[desktopFileLength - 1] = '\0';
}
void _glfwTerminateDBusPOSIX(void)
{
if(_glfw.dbus.signalName)
_glfw_free(_glfw.dbus.signalName);
if(_glfw.dbus.legalExecutableName)
_glfw_free(_glfw.dbus.legalExecutableName);
if(_glfw.dbus.fullExecutableName)
_glfw_free(_glfw.dbus.fullExecutableName);
if(_glfw.dbus.desktopFilePath)
_glfw_free(_glfw.dbus.desktopFilePath);
if (_glfw.dbus.connection)
{
dbus_connection_unref(_glfw.dbus.connection);
_glfw.dbus.connection = NULL;
}
if (_glfw.dbus.handle)
{
_glfwPlatformFreeModule(_glfw.dbus.handle);
_glfw.dbus.handle = NULL;
}
}
void _glfwUpdateTaskbarProgressDBusPOSIX(dbus_bool_t progressVisible, double progressValue)
{
struct DBusMessage* msg = NULL;
if(!_glfw.dbus.handle || !_glfw.dbus.connection || !_glfw.dbus.desktopFilePath || !_glfw.dbus.signalName)
return;
//Signal signature:
//signal com.canonical.Unity.LauncherEntry.Update (in s app_uri, in a{sv} properties)
struct DBusMessageIter args;
memset(&args, 0, sizeof(args));
if(!_glfwNewMessageSignalDBusPOSIX(_glfw.dbus.signalName, "com.canonical.Unity.LauncherEntry", "Update", &msg))
return;
dbus_message_iter_init_append(msg, &args);
//Setup app_uri parameter
_glfwAppendDataDBusPOSIX(&args, DBUS_TYPE_STRING, &_glfw.dbus.desktopFilePath);
//Set properties parameter
struct DBusMessageIter sub1;
memset(&sub1, 0, sizeof(sub1));
_glfwOpenContainerDBusPOSIX(&args, DBUS_TYPE_ARRAY, "{sv}", &sub1);
//Set progress visible property
const char* progressVisibleStr = "progress-visible";
_glfwAppendDictDataDBusPOSIX(&sub1, DBUS_TYPE_STRING, &progressVisibleStr, DBUS_TYPE_BOOLEAN, &progressVisible);
//Set progress value property
const char* progressStr = "progress";
_glfwAppendDictDataDBusPOSIX(&sub1, DBUS_TYPE_STRING, &progressStr, DBUS_TYPE_DOUBLE, &progressValue);
_glfwCloseContainerDBusPOSIX(&args, &sub1);
_glfwSendMessageDBusPOSIX(msg);
//Free the message
dbus_message_unref(msg);
}
dbus_bool_t _glfwNewMessageSignalDBusPOSIX(const char* objectPath, const char* interfaceName, const char* signalName, struct DBusMessage** outMessage)
{
if(!outMessage)
{
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to create new DBus message, output message pointer is NULL");
return GLFW_FALSE;
}
*outMessage = dbus_message_new_signal(objectPath, interfaceName, signalName);
if(!(*outMessage))
{
*outMessage = NULL;
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to create new DBus message");
return GLFW_FALSE;
}
return GLFW_TRUE;
}
dbus_bool_t _glfwOpenContainerDBusPOSIX(struct DBusMessageIter* iterator, int DBusType, const char* signature, struct DBusMessageIter* subIterator)
{
if(DBusType != DBUS_TYPE_ARRAY && DBusType != DBUS_TYPE_STRUCT_OPEN &&
DBusType != DBUS_TYPE_STRUCT_CLOSE && DBusType != DBUS_TYPE_VARIANT &&
DBusType != DBUS_TYPE_DICT_ENTRY)
{
_glfwInputError(GLFW_PLATFORM_ERROR, "Invalid DBUS container type provided");
return GLFW_FALSE;
}
if(!iterator || !subIterator)
{
_glfwInputError(GLFW_PLATFORM_ERROR, "DBus message iterator is NULL");
return GLFW_FALSE;
}
return dbus_message_iter_open_container(iterator, DBusType, signature, subIterator);
}
dbus_bool_t _glfwCloseContainerDBusPOSIX(struct DBusMessageIter* iterator, struct DBusMessageIter* subIterator)
{
if(!iterator || !subIterator)
{
_glfwInputError(GLFW_PLATFORM_ERROR, "DBus message iterator is NULL");
return GLFW_FALSE;
}
return dbus_message_iter_close_container(iterator, subIterator);
}
dbus_bool_t _glfwAppendDataDBusPOSIX(struct DBusMessageIter* iterator, int DBusType, const void* data)
{
if(DBusType == DBUS_TYPE_ARRAY || DBusType == DBUS_TYPE_VARIANT || DBusType == DBUS_TYPE_DICT_ENTRY || DBusType == DBUS_TYPE_STRUCT_OPEN || DBusType == DBUS_TYPE_STRUCT_CLOSE)
{
_glfwInputError(GLFW_PLATFORM_ERROR, "Invalid DBus type provided");
return GLFW_FALSE;
}
if(!iterator)
{
_glfwInputError(GLFW_PLATFORM_ERROR, "DBus message iterator is NULL");
return GLFW_FALSE;
}
if(!data)
{
_glfwInputError(GLFW_PLATFORM_ERROR, "DBus data to append is NULL");
return GLFW_FALSE;
}
return dbus_message_iter_append_basic(iterator, DBusType, data);
}
dbus_bool_t _glfwAppendDictDataDBusPOSIX(struct DBusMessageIter* iterator, int keyType, const void* keyData, int valueType, const void* valueData)
{
struct DBusMessageIter keyIterator;
struct DBusMessageIter valueIterator;
memset(&keyIterator, 0, sizeof(keyIterator));
memset(&valueIterator, 0, sizeof(valueIterator));
if(!_glfwOpenContainerDBusPOSIX(iterator, DBUS_TYPE_DICT_ENTRY, NULL, &keyIterator))
return GLFW_FALSE;
//Append key data
if(!_glfwAppendDataDBusPOSIX(&keyIterator, keyType, keyData))
return GLFW_FALSE;
char* valueTypeStr = _glfwDBusTypeToStrPOSIX(valueType);
assert(valueTypeStr != NULL);
if(!_glfwOpenContainerDBusPOSIX(&keyIterator, DBUS_TYPE_VARIANT, valueTypeStr, &valueIterator))
return GLFW_FALSE;
//Append value data
if(!_glfwAppendDataDBusPOSIX(&valueIterator, valueType, valueData))
return GLFW_FALSE;
if(!_glfwCloseContainerDBusPOSIX(&keyIterator, &valueIterator))
return GLFW_FALSE;
if(!_glfwCloseContainerDBusPOSIX(iterator, &keyIterator))
return GLFW_FALSE;
return GLFW_TRUE;
}
dbus_bool_t _glfwSendMessageDBusPOSIX(struct DBusMessage* message)
{
if(!message)
{
_glfwInputError(GLFW_PLATFORM_ERROR, "DBus message is NULL");
return GLFW_FALSE;
}
unsigned int serial = 0;
if(!dbus_connection_send(_glfw.dbus.connection, message, &serial))
{
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to send DBus message");
return GLFW_FALSE;
}
dbus_connection_flush(_glfw.dbus.connection);
return GLFW_TRUE;
}
char* _glfwDBusTypeToStrPOSIX(int valueType)
{
switch (valueType)
{
case DBUS_TYPE_STRING:
return DBUS_TYPE_STRING_AS_STRING;
case DBUS_TYPE_ARRAY:
return DBUS_TYPE_ARRAY_AS_STRING;
case DBUS_TYPE_DICT_ENTRY:
return DBUS_TYPE_DICT_ENTRY_AS_STRING;
case DBUS_TYPE_VARIANT:
return DBUS_TYPE_VARIANT_AS_STRING;
case DBUS_TYPE_BOOLEAN:
return DBUS_TYPE_BOOLEAN_AS_STRING;
case DBUS_TYPE_DOUBLE:
return DBUS_TYPE_DOUBLE_AS_STRING;
case DBUS_TYPE_INT16:
return DBUS_TYPE_INT16_AS_STRING;
case DBUS_TYPE_UINT16:
return DBUS_TYPE_UINT16_AS_STRING;
case DBUS_TYPE_INT32:
return DBUS_TYPE_INT32_AS_STRING;
case DBUS_TYPE_UINT32:
return DBUS_TYPE_UINT32_AS_STRING;
case DBUS_TYPE_INT64:
return DBUS_TYPE_INT64_AS_STRING;
case DBUS_TYPE_UINT64:
return DBUS_TYPE_UINT64_AS_STRING;
case DBUS_TYPE_STRUCT_OPEN:
return DBUS_TYPE_STRUCT_OPEN_AS_STRING;
case DBUS_TYPE_STRUCT_CLOSE:
return DBUS_TYPE_STRUCT_CLOSE_AS_STRING;
case DBUS_TYPE_BYTE:
return DBUS_TYPE_BYTE_AS_STRING;
case DBUS_TYPE_OBJECT_PATH:
return DBUS_TYPE_OBJECT_PATH_AS_STRING;
case DBUS_TYPE_SIGNATURE:
return DBUS_TYPE_SIGNATURE_AS_STRING;
}
return NULL;
}

177
src/posix_dbus.h Normal file
View File

@ -0,0 +1,177 @@
//========================================================================
// GLFW 3.4 POSIX - www.glfw.org
//------------------------------------------------------------------------
// Copyright (c) 2023 Camilla Löwy <elmindreda@glfw.org>
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
//
//========================================================================
//Taken from DBus docs (https://dbus.freedesktop.org/doc/api/html/index.html)
typedef struct DBusConnection DBusConnection;
typedef struct DBusMessage DBusMessage;
typedef unsigned int dbus_bool_t;
typedef unsigned int dbus_uint32_t;
enum DBusBusType
{
DBUS_BUS_SESSION,
DBUS_BUS_SYSTEM,
DBUS_BUS_STARTER
};
struct DBusError
{
const char* name;
const char* message;
unsigned int dummy1 : 1;
unsigned int dummy2 : 1;
unsigned int dummy3 : 1;
unsigned int dummy4 : 1;
unsigned int dummy5 : 1;
void* padding1;
};
struct DBusMessageIter
{
void* dummy1;
void* dummy2;
dbus_uint32_t dummy3;
int dummy4, dummy5, dummy6, dummy7, dummy8, dummy9, dummy10, dummy11;
int pad1;
void* pad2;
void* pad3;
};
#define DBUS_NAME_FLAG_REPLACE_EXISTING 0x2
#define DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER 1
#define DBUS_TYPE_STRING (unsigned int)'s'
#define DBUS_TYPE_ARRAY (unsigned int)'a'
#define DBUS_TYPE_DICT_ENTRY (unsigned int)'e'
#define DBUS_TYPE_VARIANT (unsigned int)'v'
#define DBUS_TYPE_BOOLEAN (unsigned int)'b'
#define DBUS_TYPE_DOUBLE (unsigned int)'d'
#define DBUS_TYPE_INT16 (unsigned int)'n'
#define DBUS_TYPE_UINT16 (unsigned int)'q'
#define DBUS_TYPE_INT32 (unsigned int)'i'
#define DBUS_TYPE_UINT32 (unsigned int)'u'
#define DBUS_TYPE_INT64 (unsigned int)'x'
#define DBUS_TYPE_UINT64 (unsigned int)'t'
#define DBUS_TYPE_STRUCT_OPEN (unsigned int)'('
#define DBUS_TYPE_STRUCT_CLOSE (unsigned int)')'
#define DBUS_TYPE_BYTE (unsigned int)'y'
#define DBUS_TYPE_OBJECT_PATH (unsigned int)'o'
#define DBUS_TYPE_SIGNATURE (unsigned int)'g'
#define DBUS_TYPE_STRING_AS_STRING "s"
#define DBUS_TYPE_ARRAY_AS_STRING "a"
#define DBUS_TYPE_DICT_ENTRY_AS_STRING "e"
#define DBUS_TYPE_VARIANT_AS_STRING "v"
#define DBUS_TYPE_BOOLEAN_AS_STRING "b"
#define DBUS_TYPE_DOUBLE_AS_STRING "d"
#define DBUS_TYPE_INT16_AS_STRING "n"
#define DBUS_TYPE_UINT16_AS_STRING "q"
#define DBUS_TYPE_INT32_AS_STRING "i"
#define DBUS_TYPE_UINT32_AS_STRING "u"
#define DBUS_TYPE_INT64_AS_STRING "x"
#define DBUS_TYPE_UINT64_AS_STRING "t"
#define DBUS_TYPE_STRUCT_OPEN_AS_STRING "("
#define DBUS_TYPE_STRUCT_CLOSE_AS_STRING ")"
#define DBUS_TYPE_BYTE_AS_STRING "y"
#define DBUS_TYPE_OBJECT_PATH_AS_STRING "o"
#define DBUS_TYPE_SIGNATURE_AS_STRING "g"
typedef void (* PFN_dbus_error_init)(struct DBusError*);
typedef dbus_bool_t (* PFN_dbus_error_is_set)(const struct DBusError*);
typedef void (* PFN_dbus_error_free)(struct DBusError*);
typedef void (* PFN_dbus_connection_unref)(DBusConnection*);
typedef dbus_bool_t (* PFN_dbus_connection_send)(DBusConnection*, DBusMessage*, dbus_uint32_t*);
typedef void (* PFN_dbus_connection_flush)(DBusConnection*);
typedef int (* PFN_dbus_bus_request_name)(DBusConnection*, const char*, unsigned int, struct DBusError*);
typedef DBusConnection* (* PFN_dbus_bus_get)(enum DBusBusType, struct DBusError*);
typedef void (* PFN_dbus_message_unref)(DBusMessage*);
typedef DBusMessage* (* PFN_dbus_message_new_signal)(const char*, const char*, const char*);
typedef void (* PFN_dbus_message_iter_init_append)(DBusMessage*, struct DBusMessageIter*);
typedef dbus_bool_t (* PFN_dbus_message_iter_append_basic)(struct DBusMessageIter*, int, const void*);
typedef dbus_bool_t (* PFN_dbus_message_iter_open_container)(struct DBusMessageIter*, int, const char*, struct DBusMessageIter*);
typedef dbus_bool_t (* PFN_dbus_message_iter_close_container)(struct DBusMessageIter*, struct DBusMessageIter*);
#define dbus_error_init _glfw.dbus.error_init
#define dbus_error_is_set _glfw.dbus.error_is_set
#define dbus_error_free _glfw.dbus.error_free
#define dbus_connection_unref _glfw.dbus.connection_unref
#define dbus_connection_send _glfw.dbus.connection_send
#define dbus_connection_flush _glfw.dbus.connection_flush
#define dbus_bus_request_name _glfw.dbus.bus_request_name
#define dbus_bus_get _glfw.dbus.bus_get
#define dbus_message_unref _glfw.dbus.message_unref
#define dbus_message_new_signal _glfw.dbus.message_new_signal
#define dbus_message_iter_init_append _glfw.dbus.message_iter_init_append
#define dbus_message_iter_append_basic _glfw.dbus.message_iter_append_basic
#define dbus_message_iter_open_container _glfw.dbus.message_iter_open_container
#define dbus_message_iter_close_container _glfw.dbus.message_iter_close_container
#define GLFW_POSIX_LIBRARY_DBUS_STATE _GLFWDBusPOSIX dbus;
// POSIX-specific dbus data
//
typedef struct _GLFWDBusPOSIX
{
void* handle;
PFN_dbus_error_init error_init;
PFN_dbus_error_is_set error_is_set;
PFN_dbus_error_free error_free;
PFN_dbus_connection_unref connection_unref;
PFN_dbus_connection_send connection_send;
PFN_dbus_connection_flush connection_flush;
PFN_dbus_bus_request_name bus_request_name;
PFN_dbus_bus_get bus_get;
PFN_dbus_message_unref message_unref;
PFN_dbus_message_new_signal message_new_signal;
PFN_dbus_message_iter_init_append message_iter_init_append;
PFN_dbus_message_iter_append_basic message_iter_append_basic;
PFN_dbus_message_iter_open_container message_iter_open_container;
PFN_dbus_message_iter_close_container message_iter_close_container;
DBusConnection* connection;
struct DBusError error;
char* desktopFilePath;
char* fullExecutableName;
char* legalExecutableName;
char* signalName;
} _GLFWDBusPOSIX;
void _glfwInitDBusPOSIX(void);
void _glfwCacheSignalNameDBusPOSIX(void);
void _glfwCacheFullExecutableNameDBusPOSIX(void);
void _glfwCacheLegalExecutableNameDBusPOSIX(void);
void _glfwCacheDesktopFilePathDBusPOSIX(void);
void _glfwTerminateDBusPOSIX(void);
void _glfwUpdateTaskbarProgressDBusPOSIX(dbus_bool_t progressVisible, double progressValue);
dbus_bool_t _glfwNewMessageSignalDBusPOSIX(const char* objectPath, const char* interfaceName, const char* signalName, struct DBusMessage** outMessage);
dbus_bool_t _glfwOpenContainerDBusPOSIX(struct DBusMessageIter* iterator, int DBusType, const char* signature, struct DBusMessageIter* subIterator);
dbus_bool_t _glfwCloseContainerDBusPOSIX(struct DBusMessageIter* iterator, struct DBusMessageIter* subIterator);
dbus_bool_t _glfwAppendDataDBusPOSIX(struct DBusMessageIter* iterator, int DBusType, const void* data);
dbus_bool_t _glfwAppendDictDataDBusPOSIX(struct DBusMessageIter* iterator, int keyType, const void* keyData, int valueType, const void* valueData);
dbus_bool_t _glfwSendMessageDBusPOSIX(struct DBusMessage* message);
char* _glfwDBusTypeToStrPOSIX(int valueType);

View File

@ -528,7 +528,8 @@ void _glfwUpdateKeyNamesWin32(void)
if (key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_ADD) if (key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_ADD)
{ {
const UINT vks[] = { const UINT vks[] =
{
VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3,
VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7, VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7,
VK_NUMPAD8, VK_NUMPAD9, VK_DECIMAL, VK_DIVIDE, VK_NUMPAD8, VK_NUMPAD9, VK_DECIMAL, VK_DIVIDE,
@ -631,6 +632,7 @@ GLFWbool _glfwConnectWin32(int platformID, _GLFWplatform* platform)
.destroyWindow = _glfwDestroyWindowWin32, .destroyWindow = _glfwDestroyWindowWin32,
.setWindowTitle = _glfwSetWindowTitleWin32, .setWindowTitle = _glfwSetWindowTitleWin32,
.setWindowIcon = _glfwSetWindowIconWin32, .setWindowIcon = _glfwSetWindowIconWin32,
.setWindowProgressIndicator = _glfwSetWindowProgressIndicatorWin32,
.getWindowPos = _glfwGetWindowPosWin32, .getWindowPos = _glfwGetWindowPosWin32,
.setWindowPos = _glfwSetWindowPosWin32, .setWindowPos = _glfwSetWindowPosWin32,
.getWindowSize = _glfwGetWindowSizeWin32, .getWindowSize = _glfwGetWindowSizeWin32,

View File

@ -308,6 +308,79 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)(
#define GLFW_WGL_CONTEXT_STATE _GLFWcontextWGL wgl; #define GLFW_WGL_CONTEXT_STATE _GLFWcontextWGL wgl;
#define GLFW_WGL_LIBRARY_CONTEXT_STATE _GLFWlibraryWGL wgl; #define GLFW_WGL_LIBRARY_CONTEXT_STATE _GLFWlibraryWGL wgl;
typedef enum
{
TBPF_NOPROGRESS = 0x0,
TBPF_INDETERMINATE = 0x1,
TBPF_NORMAL = 0x2,
TBPF_ERROR = 0x4,
TBPF_PAUSED = 0x8
} TBPFLAG;
static const IID IID_ITaskbarList3 = { 0xea1afb91, 0x9e28, 0x4b86, {0x90, 0xe9, 0x9e, 0x9f, 0x8a, 0x5e, 0xef, 0xaf} };
static const IID CLSID_TaskbarList = { 0x56fdf344, 0xfd6d, 0x11d0, {0x95, 0x8a, 0x00, 0x60, 0x97, 0xc9, 0xa0, 0x90} };
typedef enum THUMBBUTTONMASK
{
THB_BITMAP = 0x1,
THB_ICON = 0x2,
THB_TOOLTIP = 0x4,
THB_FLAGS = 0x8
} THUMBBUTTONMASK;
typedef enum THUMBBUTTONFLAGS
{
THBF_ENABLED = 0,
THBF_DISABLED = 0x1,
THBF_DISMISSONCLICK = 0x2,
THBF_NOBACKGROUND = 0x4,
THBF_HIDDEN = 0x8,
THBF_NONINTERACTIVE = 0x10
} THUMBBUTTONFLAGS;
typedef struct THUMBBUTTON {
THUMBBUTTONMASK dwMask;
UINT iId;
UINT iBitmap;
HICON hIcon;
WCHAR szTip[260];
THUMBBUTTONFLAGS dwFlags;
} THUMBBUTTON, *LPTHUMBBUTTON;
struct _IMAGELIST;
typedef struct _IMAGELIST* HIMAGELIST;
typedef struct ITaskbarList3 ITaskbarList3;
typedef struct ITaskbarList3Vtbl
{
HRESULT(WINAPI* QueryInterface)(struct ITaskbarList3*, const IID* const, void**);
ULONG(WINAPI* AddRef)(struct ITaskbarList3*);
ULONG(WINAPI* Release)(struct ITaskbarList3*);
HRESULT(WINAPI* HrInit)(struct ITaskbarList3*);
HRESULT(WINAPI* AddTab)(struct ITaskbarList3*, HWND);
HRESULT(WINAPI* DeleteTab)(struct ITaskbarList3*, HWND);
HRESULT(WINAPI* ActivateTab)(struct ITaskbarList3*, HWND);
HRESULT(WINAPI* SetActiveAlt)(struct ITaskbarList3*, HWND);
HRESULT(WINAPI* MarkFullscreenWindow)(struct ITaskbarList3*, HWND, BOOL);
HRESULT(WINAPI* SetProgressValue)(struct ITaskbarList3*, HWND, ULONGLONG, ULONGLONG);
HRESULT(WINAPI* SetProgressState)(struct ITaskbarList3*, HWND, TBPFLAG);
HRESULT(WINAPI* RegisterTab)(struct ITaskbarList3*, HWND, HWND);
HRESULT(WINAPI* UnregisterTab)(struct ITaskbarList3*, HWND);
HRESULT(WINAPI* SetTabOrder)(struct ITaskbarList3*, HWND, HWND);
HRESULT(WINAPI* SetTabActive)(struct ITaskbarList3*, HWND, HWND, DWORD);
HRESULT(WINAPI* ThumbBarAddButtons)(struct ITaskbarList3*, HWND, UINT, LPTHUMBBUTTON);
HRESULT(WINAPI* ThumbBarUpdateButtons)(struct ITaskbarList3*, HWND, UINT, LPTHUMBBUTTON);
HRESULT(WINAPI* ThumbBarSetImageList)(struct ITaskbarList3*, HWND, HIMAGELIST);
HRESULT(WINAPI* SetOverlayIcon)(struct ITaskbarList3*, HWND, HICON, LPCWSTR);
HRESULT(WINAPI* SetThumbnailTooltip)(struct ITaskbarList3*, HWND, LPCWSTR);
HRESULT(WINAPI* SetThumbnailClip)(struct ITaskbarList3*, HWND, RECT*);
} ITaskbarList3Vtbl;
struct ITaskbarList3
{
struct ITaskbarList3Vtbl* lpVtbl;
};
// WGL-specific per-context data // WGL-specific per-context data
// //
@ -375,6 +448,9 @@ typedef struct _GLFWwindowWin32
int lastCursorPosX, lastCursorPosY; int lastCursorPosX, lastCursorPosY;
// The last received high surrogate when decoding pairs of UTF-16 messages // The last received high surrogate when decoding pairs of UTF-16 messages
WCHAR highSurrogate; WCHAR highSurrogate;
ITaskbarList3* taskbarList;
UINT taskbarListMsgID;
} _GLFWwindowWin32; } _GLFWwindowWin32;
// Win32-specific global data // Win32-specific global data
@ -465,7 +541,6 @@ typedef struct _GLFWcursorWin32
HCURSOR handle; HCURSOR handle;
} _GLFWcursorWin32; } _GLFWcursorWin32;
GLFWbool _glfwConnectWin32(int platformID, _GLFWplatform* platform); GLFWbool _glfwConnectWin32(int platformID, _GLFWplatform* platform);
int _glfwInitWin32(void); int _glfwInitWin32(void);
void _glfwTerminateWin32(void); void _glfwTerminateWin32(void);
@ -486,6 +561,7 @@ GLFWbool _glfwCreateWindowWin32(_GLFWwindow* window, const _GLFWwndconfig* wndco
void _glfwDestroyWindowWin32(_GLFWwindow* window); void _glfwDestroyWindowWin32(_GLFWwindow* window);
void _glfwSetWindowTitleWin32(_GLFWwindow* window, const char* title); void _glfwSetWindowTitleWin32(_GLFWwindow* window, const char* title);
void _glfwSetWindowIconWin32(_GLFWwindow* window, int count, const GLFWimage* images); void _glfwSetWindowIconWin32(_GLFWwindow* window, int count, const GLFWimage* images);
void _glfwSetWindowProgressIndicatorWin32(_GLFWwindow* window, int progressState, double value);
void _glfwGetWindowPosWin32(_GLFWwindow* window, int* xpos, int* ypos); void _glfwGetWindowPosWin32(_GLFWwindow* window, int* xpos, int* ypos);
void _glfwSetWindowPosWin32(_GLFWwindow* window, int xpos, int ypos); void _glfwSetWindowPosWin32(_GLFWwindow* window, int xpos, int ypos);
void _glfwGetWindowSizeWin32(_GLFWwindow* window, int* width, int* height); void _glfwGetWindowSizeWin32(_GLFWwindow* window, int* width, int* height);

View File

@ -1264,6 +1264,18 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l
} }
} }
if(uMsg == window->win32.taskbarListMsgID)
{
HRESULT res = CoCreateInstance(&CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, &IID_ITaskbarList3, (LPVOID*)&window->win32.taskbarList);
if (res != S_OK && window->win32.taskbarList)
window->win32.taskbarList->lpVtbl->Release(window->win32.taskbarList);
else
{
window->win32.taskbarList->lpVtbl->AddRef(window->win32.taskbarList);
window->win32.taskbarList->lpVtbl->HrInit(window->win32.taskbarList);
}
}
return DefWindowProcW(hWnd, uMsg, wParam, lParam); return DefWindowProcW(hWnd, uMsg, wParam, lParam);
} }
@ -1407,6 +1419,10 @@ static int createNativeWindow(_GLFWwindow* window,
ChangeWindowMessageFilterEx(window->win32.handle, WM_COPYDATA, MSGFLT_ALLOW, NULL); ChangeWindowMessageFilterEx(window->win32.handle, WM_COPYDATA, MSGFLT_ALLOW, NULL);
ChangeWindowMessageFilterEx(window->win32.handle, WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL); ChangeWindowMessageFilterEx(window->win32.handle, WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL);
window->win32.taskbarListMsgID = RegisterWindowMessageW(L"TaskbarButtonCreated");
if (window->win32.taskbarListMsgID)
ChangeWindowMessageFilterEx(window->win32.handle, window->win32.taskbarListMsgID, MSGFLT_ALLOW, NULL);
window->win32.scaleToMonitor = wndconfig->scaleToMonitor; window->win32.scaleToMonitor = wndconfig->scaleToMonitor;
window->win32.keymenu = wndconfig->win32.keymenu; window->win32.keymenu = wndconfig->win32.keymenu;
window->win32.showDefault = wndconfig->win32.showDefault; window->win32.showDefault = wndconfig->win32.showDefault;
@ -1558,6 +1574,9 @@ void _glfwDestroyWindowWin32(_GLFWwindow* window)
if (_glfw.win32.capturedCursorWindow == window) if (_glfw.win32.capturedCursorWindow == window)
releaseCursor(); releaseCursor();
if (window->win32.taskbarList)
window->win32.taskbarList->lpVtbl->Release(window->win32.taskbarList);
if (window->win32.handle) if (window->win32.handle)
{ {
RemovePropW(window->win32.handle, L"GLFW"); RemovePropW(window->win32.handle, L"GLFW");
@ -1620,6 +1639,47 @@ void _glfwSetWindowIconWin32(_GLFWwindow* window, int count, const GLFWimage* im
} }
} }
void _glfwSetWindowProgressIndicatorWin32(_GLFWwindow* window, int progressState, double value)
{
HRESULT res = S_OK;
int winProgressState = 0;
int progressValue = (int)(value * 100.0);
if(!window->win32.taskbarList)
return;
res = window->win32.taskbarList->lpVtbl->SetProgressValue(window->win32.taskbarList, window->win32.handle, progressValue, 100);
if(res != S_OK)
{
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to set taskbar progress value");
return;
}
switch(progressState)
{
case GLFW_PROGRESS_INDICATOR_INDETERMINATE:
winProgressState = TBPF_INDETERMINATE;
break;
case GLFW_PROGRESS_INDICATOR_NORMAL:
winProgressState = TBPF_NORMAL;
break;
case GLFW_PROGRESS_INDICATOR_ERROR:
winProgressState = TBPF_ERROR;
break;
case GLFW_PROGRESS_INDICATOR_PAUSED:
winProgressState = TBPF_PAUSED;
break;
default:
winProgressState = TBPF_NOPROGRESS;
break;
}
res = window->win32.taskbarList->lpVtbl->SetProgressState(window->win32.taskbarList, window->win32.handle, winProgressState);
if (res != S_OK)
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to set taskbar progress state");
}
void _glfwGetWindowPosWin32(_GLFWwindow* window, int* xpos, int* ypos) void _glfwGetWindowPosWin32(_GLFWwindow* window, int* xpos, int* ypos)
{ {
POINT pos = { 0, 0 }; POINT pos = { 0, 0 };

View File

@ -578,6 +578,31 @@ GLFWAPI void glfwSetWindowIcon(GLFWwindow* handle,
_glfw.platform.setWindowIcon(window, count, images); _glfw.platform.setWindowIcon(window, count, images);
} }
GLFWAPI void glfwSetWindowProgressIndicator(GLFWwindow* handle, int progressState, double value)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
assert(window != NULL);
_GLFW_REQUIRE_INIT();
if (value < 0.0 || value > 1.0)
{
_glfwInputError(GLFW_INVALID_VALUE, "Invalid progress amount for window progress indicator");
return;
}
if (progressState != GLFW_PROGRESS_INDICATOR_DISABLED && progressState != GLFW_PROGRESS_INDICATOR_INDETERMINATE &&
progressState != GLFW_PROGRESS_INDICATOR_NORMAL && progressState != GLFW_PROGRESS_INDICATOR_ERROR &&
progressState != GLFW_PROGRESS_INDICATOR_PAUSED)
{
_glfwInputError(GLFW_INVALID_ENUM, "Invalid progress state 0x%08X", progressState);
return;
}
_glfw.platform.setWindowProgressIndicator(window, progressState, value);
}
GLFWAPI void glfwGetWindowPos(GLFWwindow* handle, int* xpos, int* ypos) GLFWAPI void glfwGetWindowPos(GLFWwindow* handle, int* xpos, int* ypos)
{ {
if (xpos) if (xpos)

View File

@ -477,6 +477,7 @@ GLFWbool _glfwConnectWayland(int platformID, _GLFWplatform* platform)
.destroyWindow = _glfwDestroyWindowWayland, .destroyWindow = _glfwDestroyWindowWayland,
.setWindowTitle = _glfwSetWindowTitleWayland, .setWindowTitle = _glfwSetWindowTitleWayland,
.setWindowIcon = _glfwSetWindowIconWayland, .setWindowIcon = _glfwSetWindowIconWayland,
.setWindowProgressIndicator = _glfwSetWindowProgressIndicatorWayland,
.getWindowPos = _glfwGetWindowPosWayland, .getWindowPos = _glfwGetWindowPosWayland,
.setWindowPos = _glfwSetWindowPosWayland, .setWindowPos = _glfwSetWindowPosWayland,
.getWindowSize = _glfwGetWindowSizeWayland, .getWindowSize = _glfwGetWindowSizeWayland,
@ -563,6 +564,8 @@ GLFWbool _glfwConnectWayland(int platformID, _GLFWplatform* platform)
int _glfwInitWayland(void) int _glfwInitWayland(void)
{ {
_glfwInitDBusPOSIX();
// These must be set before any failure checks // These must be set before any failure checks
_glfw.wl.keyRepeatTimerfd = -1; _glfw.wl.keyRepeatTimerfd = -1;
_glfw.wl.cursorTimerfd = -1; _glfw.wl.cursorTimerfd = -1;
@ -979,7 +982,8 @@ void _glfwTerminateWayland(void)
if (_glfw.wl.cursorTimerfd >= 0) if (_glfw.wl.cursorTimerfd >= 0)
close(_glfw.wl.cursorTimerfd); close(_glfw.wl.cursorTimerfd);
// Free modules only after all wayland termination functions are called // Free modules only after all Wayland termination functions are called
if (_glfw.egl.handle) if (_glfw.egl.handle)
{ {
_glfwPlatformFreeModule(_glfw.egl.handle); _glfwPlatformFreeModule(_glfw.egl.handle);
@ -1003,6 +1007,7 @@ void _glfwTerminateWayland(void)
_glfwPlatformFreeModule(_glfw.wl.xkb.handle); _glfwPlatformFreeModule(_glfw.wl.xkb.handle);
_glfw.wl.xkb.handle = NULL; _glfw.wl.xkb.handle = NULL;
} }
if (_glfw.wl.cursor.handle) if (_glfw.wl.cursor.handle)
{ {
_glfwPlatformFreeModule(_glfw.wl.cursor.handle); _glfwPlatformFreeModule(_glfw.wl.cursor.handle);
@ -1010,6 +1015,8 @@ void _glfwTerminateWayland(void)
} }
_glfw_free(_glfw.wl.clipboardString); _glfw_free(_glfw.wl.clipboardString);
_glfwTerminateDBusPOSIX();
} }
#endif // _GLFW_WAYLAND #endif // _GLFW_WAYLAND

View File

@ -136,18 +136,22 @@ struct wl_output;
#define GLFW_WAYLAND_MONITOR_STATE _GLFWmonitorWayland wl; #define GLFW_WAYLAND_MONITOR_STATE _GLFWmonitorWayland wl;
#define GLFW_WAYLAND_CURSOR_STATE _GLFWcursorWayland wl; #define GLFW_WAYLAND_CURSOR_STATE _GLFWcursorWayland wl;
struct wl_cursor_image { struct wl_cursor_image
{
uint32_t width; uint32_t width;
uint32_t height; uint32_t height;
uint32_t hotspot_x; uint32_t hotspot_x;
uint32_t hotspot_y; uint32_t hotspot_y;
uint32_t delay; uint32_t delay;
}; };
struct wl_cursor {
struct wl_cursor
{
unsigned int image_count; unsigned int image_count;
struct wl_cursor_image** images; struct wl_cursor_image** images;
char* name; char* name;
}; };
typedef struct wl_cursor_theme* (* PFN_wl_cursor_theme_load)(const char*, int, struct wl_shm*); typedef struct wl_cursor_theme* (* PFN_wl_cursor_theme_load)(const char*, int, struct wl_shm*);
typedef void (* PFN_wl_cursor_theme_destroy)(struct wl_cursor_theme*); typedef void (* PFN_wl_cursor_theme_destroy)(struct wl_cursor_theme*);
typedef struct wl_cursor* (* PFN_wl_cursor_theme_get_cursor)(struct wl_cursor_theme*, const char*); typedef struct wl_cursor* (* PFN_wl_cursor_theme_get_cursor)(struct wl_cursor_theme*, const char*);
@ -620,6 +624,7 @@ GLFWbool _glfwCreateWindowWayland(_GLFWwindow* window, const _GLFWwndconfig* wnd
void _glfwDestroyWindowWayland(_GLFWwindow* window); void _glfwDestroyWindowWayland(_GLFWwindow* window);
void _glfwSetWindowTitleWayland(_GLFWwindow* window, const char* title); void _glfwSetWindowTitleWayland(_GLFWwindow* window, const char* title);
void _glfwSetWindowIconWayland(_GLFWwindow* window, int count, const GLFWimage* images); void _glfwSetWindowIconWayland(_GLFWwindow* window, int count, const GLFWimage* images);
void _glfwSetWindowProgressIndicatorWayland(_GLFWwindow* window, int progressState, double value);
void _glfwGetWindowPosWayland(_GLFWwindow* window, int* xpos, int* ypos); void _glfwGetWindowPosWayland(_GLFWwindow* window, int* xpos, int* ypos);
void _glfwSetWindowPosWayland(_GLFWwindow* window, int xpos, int ypos); void _glfwSetWindowPosWayland(_GLFWwindow* window, int xpos, int ypos);
void _glfwGetWindowSizeWayland(_GLFWwindow* window, int* width, int* height); void _glfwGetWindowSizeWayland(_GLFWwindow* window, int* width, int* height);

View File

@ -1413,7 +1413,7 @@ static void handleEvents(double* timeout)
if (read(_glfw.wl.keyRepeatTimerfd, &repeats, sizeof(repeats)) == 8) if (read(_glfw.wl.keyRepeatTimerfd, &repeats, sizeof(repeats)) == 8)
{ {
if(_glfw.wl.keyboardFocus) if (_glfw.wl.keyboardFocus)
{ {
for (uint64_t i = 0; i < repeats; i++) for (uint64_t i = 0; i < repeats; i++)
{ {
@ -1692,7 +1692,8 @@ static void keyboardHandleKeymap(void* userData,
} }
mapStr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); mapStr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
if (mapStr == MAP_FAILED) { if (mapStr == MAP_FAILED)
{
close(fd); close(fd);
return; return;
} }
@ -1838,7 +1839,9 @@ static void keyboardHandleKey(void* userData,
timer.it_value.tv_nsec = (_glfw.wl.keyRepeatDelay % 1000) * 1000000; timer.it_value.tv_nsec = (_glfw.wl.keyRepeatDelay % 1000) * 1000000;
timerfd_settime(_glfw.wl.keyRepeatTimerfd, 0, &timer, NULL); timerfd_settime(_glfw.wl.keyRepeatTimerfd, 0, &timer, NULL);
} }
} else if (scancode == _glfw.wl.keyRepeatScancode) { }
else if (scancode == _glfw.wl.keyRepeatScancode)
{
timerfd_settime(_glfw.wl.keyRepeatTimerfd, 0, &timer, NULL); timerfd_settime(_glfw.wl.keyRepeatTimerfd, 0, &timer, NULL);
} }
@ -2215,6 +2218,14 @@ GLFWbool _glfwCreateWindowWayland(_GLFWwindow* window,
return GLFW_FALSE; return GLFW_FALSE;
} }
//Reset progress state as it gets saved between application runs
if(_glfw.dbus.connection)
{
//Window NULL is safe here because it won't get
//used inside the SetWindowTaskbarProgress function
_glfwSetWindowProgressIndicatorWayland(NULL, GLFW_PROGRESS_INDICATOR_DISABLED, 0.0);
}
return GLFW_TRUE; return GLFW_TRUE;
} }
@ -2285,6 +2296,15 @@ void _glfwSetWindowIconWayland(_GLFWwindow* window,
"Wayland: The platform does not support setting the window icon"); "Wayland: The platform does not support setting the window icon");
} }
void _glfwSetWindowProgressIndicatorWayland(_GLFWwindow* window, const int progressState, double value)
{
(void)window;
const dbus_bool_t progressVisible = (progressState != GLFW_PROGRESS_INDICATOR_DISABLED);
_glfwUpdateTaskbarProgressDBusPOSIX(progressVisible, value);
}
void _glfwGetWindowPosWayland(_GLFWwindow* window, int* xpos, int* ypos) void _glfwGetWindowPosWayland(_GLFWwindow* window, int* xpos, int* ypos)
{ {
// A Wayland client is not aware of its position, so just warn and leave it // A Wayland client is not aware of its position, so just warn and leave it
@ -2974,10 +2994,16 @@ static void lockPointer(_GLFWwindow* window)
if (!_glfw.wl.relativePointerManager) if (!_glfw.wl.relativePointerManager)
{ {
_glfwInputError(GLFW_FEATURE_UNAVAILABLE, _glfwInputError(GLFW_FEATURE_UNAVAILABLE,
"Wayland: The compositor does not support pointer locking"); "Wayland: The compositor does not support relative pointer motion");
return; return;
} }
if (!_glfw.wl.pointerConstraints)
{
_glfwInputError(GLFW_FEATURE_UNAVAILABLE,
"Wayland: The compositor does not support locking the pointer");
}
window->wl.relativePointer = window->wl.relativePointer =
zwp_relative_pointer_manager_v1_get_relative_pointer( zwp_relative_pointer_manager_v1_get_relative_pointer(
_glfw.wl.relativePointerManager, _glfw.wl.relativePointerManager,
@ -3025,6 +3051,12 @@ static const struct zwp_confined_pointer_v1_listener confinedPointerListener =
static void confinePointer(_GLFWwindow* window) static void confinePointer(_GLFWwindow* window)
{ {
if (!_glfw.wl.pointerConstraints)
{
_glfwInputError(GLFW_FEATURE_UNAVAILABLE,
"Wayland: The compositor does not support confining the pointer");
}
window->wl.confinedPointer = window->wl.confinedPointer =
zwp_pointer_constraints_v1_confine_pointer( zwp_pointer_constraints_v1_confine_pointer(
_glfw.wl.pointerConstraints, _glfw.wl.pointerConstraints,

View File

@ -1208,6 +1208,7 @@ GLFWbool _glfwConnectX11(int platformID, _GLFWplatform* platform)
.destroyWindow = _glfwDestroyWindowX11, .destroyWindow = _glfwDestroyWindowX11,
.setWindowTitle = _glfwSetWindowTitleX11, .setWindowTitle = _glfwSetWindowTitleX11,
.setWindowIcon = _glfwSetWindowIconX11, .setWindowIcon = _glfwSetWindowIconX11,
.setWindowProgressIndicator = _glfwSetWindowProgressIndicatorX11,
.getWindowPos = _glfwGetWindowPosX11, .getWindowPos = _glfwGetWindowPosX11,
.setWindowPos = _glfwSetWindowPosX11, .setWindowPos = _glfwSetWindowPosX11,
.getWindowSize = _glfwGetWindowSizeX11, .getWindowSize = _glfwGetWindowSizeX11,
@ -1320,6 +1321,8 @@ GLFWbool _glfwConnectX11(int platformID, _GLFWplatform* platform)
int _glfwInitX11(void) int _glfwInitX11(void)
{ {
_glfwInitDBusPOSIX();
_glfw.x11.xlib.AllocClassHint = (PFN_XAllocClassHint) _glfw.x11.xlib.AllocClassHint = (PFN_XAllocClassHint)
_glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XAllocClassHint"); _glfwPlatformGetModuleSymbol(_glfw.x11.xlib.handle, "XAllocClassHint");
_glfw.x11.xlib.AllocSizeHints = (PFN_XAllocSizeHints) _glfw.x11.xlib.AllocSizeHints = (PFN_XAllocSizeHints)
@ -1651,6 +1654,8 @@ void _glfwTerminateX11(void)
close(_glfw.x11.emptyEventPipe[0]); close(_glfw.x11.emptyEventPipe[0]);
close(_glfw.x11.emptyEventPipe[1]); close(_glfw.x11.emptyEventPipe[1]);
} }
_glfwTerminateDBusPOSIX();
} }
#endif // _GLFW_X11 #endif // _GLFW_X11

View File

@ -151,7 +151,8 @@ void _glfwPollMonitorsX11(void)
} }
XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, oi->crtc); XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, oi->crtc);
if (!ci) { if (!ci)
{
XRRFreeOutputInfo(oi); XRRFreeOutputInfo(oi);
continue; continue;
} }

View File

@ -906,6 +906,7 @@ GLFWbool _glfwCreateWindowX11(_GLFWwindow* window, const _GLFWwndconfig* wndconf
void _glfwDestroyWindowX11(_GLFWwindow* window); void _glfwDestroyWindowX11(_GLFWwindow* window);
void _glfwSetWindowTitleX11(_GLFWwindow* window, const char* title); void _glfwSetWindowTitleX11(_GLFWwindow* window, const char* title);
void _glfwSetWindowIconX11(_GLFWwindow* window, int count, const GLFWimage* images); void _glfwSetWindowIconX11(_GLFWwindow* window, int count, const GLFWimage* images);
void _glfwSetWindowProgressIndicatorX11(_GLFWwindow* window, int progressState, double value);
void _glfwGetWindowPosX11(_GLFWwindow* window, int* xpos, int* ypos); void _glfwGetWindowPosX11(_GLFWwindow* window, int* xpos, int* ypos);
void _glfwSetWindowPosX11(_GLFWwindow* window, int xpos, int ypos); void _glfwSetWindowPosX11(_GLFWwindow* window, int xpos, int ypos);
void _glfwGetWindowSizeX11(_GLFWwindow* window, int* width, int* height); void _glfwGetWindowSizeX11(_GLFWwindow* window, int* width, int* height);

View File

@ -2044,6 +2044,14 @@ GLFWbool _glfwCreateWindowX11(_GLFWwindow* window,
} }
} }
//Reset progress state as it gets saved between application runs
if(_glfw.dbus.connection)
{
//Window NULL is safe here because it won't get
//used inside the SetWindowTaskbarProgress function
_glfwSetWindowProgressIndicatorX11(NULL, GLFW_PROGRESS_INDICATOR_DISABLED, 0.0);
}
XFlush(_glfw.x11.display); XFlush(_glfw.x11.display);
return GLFW_TRUE; return GLFW_TRUE;
} }
@ -2156,6 +2164,15 @@ void _glfwSetWindowIconX11(_GLFWwindow* window, int count, const GLFWimage* imag
XFlush(_glfw.x11.display); XFlush(_glfw.x11.display);
} }
void _glfwSetWindowProgressIndicatorX11(_GLFWwindow* window, int progressState, double value)
{
(void)window;
const dbus_bool_t progressVisible = (progressState != GLFW_PROGRESS_INDICATOR_DISABLED);
_glfwUpdateTaskbarProgressDBusPOSIX(progressVisible, value);
}
void _glfwGetWindowPosX11(_GLFWwindow* window, int* xpos, int* ypos) void _glfwGetWindowPosX11(_GLFWwindow* window, int* xpos, int* ypos)
{ {
Window dummy; Window dummy;
@ -2207,10 +2224,10 @@ void _glfwGetWindowSizeX11(_GLFWwindow* window, int* width, int* height)
void _glfwSetWindowSizeX11(_GLFWwindow* window, int width, int height) void _glfwSetWindowSizeX11(_GLFWwindow* window, int width, int height)
{ {
// The dimensions must be nonzero, or a BadValue error results. // The dimensions must be nonzero, or a BadValue error results
width = _glfw_max(1, width); width = _glfw_max(1, width);
height = _glfw_max(1, height); height = _glfw_max(1, height);
if (window->monitor) if (window->monitor)
{ {
if (window->monitor->window == window) if (window->monitor->window == window)

View File

@ -72,7 +72,7 @@ int main(int argc, char** argv)
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
GLFWwindow* window = glfwCreateWindow(600, 660, "Window Features", NULL, NULL); GLFWwindow* window = glfwCreateWindow(600, 700, "Window Features", NULL, NULL);
if (!window) if (!window)
{ {
glfwTerminate(); glfwTerminate();
@ -441,6 +441,29 @@ int main(int argc, char** argv)
nk_value_bool(nk, "Visible", glfwGetWindowAttrib(window, GLFW_VISIBLE)); nk_value_bool(nk, "Visible", glfwGetWindowAttrib(window, GLFW_VISIBLE));
nk_value_bool(nk, "Iconified", glfwGetWindowAttrib(window, GLFW_ICONIFIED)); nk_value_bool(nk, "Iconified", glfwGetWindowAttrib(window, GLFW_ICONIFIED));
nk_value_bool(nk, "Maximized", glfwGetWindowAttrib(window, GLFW_MAXIMIZED)); nk_value_bool(nk, "Maximized", glfwGetWindowAttrib(window, GLFW_MAXIMIZED));
nk_layout_row_dynamic(nk, 30, 1);
nk_label(nk, "Window Progress indicator", NK_TEXT_CENTERED);
nk_layout_row_dynamic(nk, 30, 5);
static int state = GLFW_PROGRESS_INDICATOR_DISABLED;
static float progress = 0;
if(nk_button_label(nk, "No progress"))
glfwSetWindowProgressIndicator(window, state = GLFW_PROGRESS_INDICATOR_DISABLED, (double) progress);
if (nk_button_label(nk, "Indeterminate"))
glfwSetWindowProgressIndicator(window, state = GLFW_PROGRESS_INDICATOR_INDETERMINATE, (double) progress);
if (nk_button_label(nk, "Normal"))
glfwSetWindowProgressIndicator(window, state = GLFW_PROGRESS_INDICATOR_NORMAL, (double) progress);
if (nk_button_label(nk, "Error"))
glfwSetWindowProgressIndicator(window, state = GLFW_PROGRESS_INDICATOR_ERROR, (double) progress);
if (nk_button_label(nk, "Paused"))
glfwSetWindowProgressIndicator(window, state = GLFW_PROGRESS_INDICATOR_PAUSED, (double) progress);
nk_label(nk, "Progress: ", NK_TEXT_ALIGN_LEFT);
if (nk_slider_float(nk, 0.0f, &progress, 1.0f, 0.05f))
glfwSetWindowProgressIndicator(window, state, (double) progress);
} }
nk_end(nk); nk_end(nk);