mirror of
https://github.com/glfw/glfw.git
synced 2024-11-22 21:14:35 +00:00
Merge 1226d4a839
into d79afa8055
This commit is contained in:
commit
9f521e0778
@ -103,6 +103,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
|
||||||
@ -202,6 +203,7 @@ video tutorials.
|
|||||||
- Guillaume Racicot
|
- Guillaume Racicot
|
||||||
- Juan Ramos
|
- Juan Ramos
|
||||||
- Christian Rauch
|
- Christian Rauch
|
||||||
|
- Simon Richter
|
||||||
- Philip Rideout
|
- Philip Rideout
|
||||||
- Eddie Ringle
|
- Eddie Ringle
|
||||||
- Max Risuhin
|
- Max Risuhin
|
||||||
@ -216,6 +218,7 @@ video tutorials.
|
|||||||
- Yoshinori Sano
|
- Yoshinori Sano
|
||||||
- Brandon Schaefer
|
- Brandon Schaefer
|
||||||
- Sebastian Schuberth
|
- Sebastian Schuberth
|
||||||
|
- Jan Schuerkamp
|
||||||
- Scr3amer
|
- Scr3amer
|
||||||
- Christian Sdunek
|
- Christian Sdunek
|
||||||
- Matt Sealey
|
- Matt Sealey
|
||||||
|
@ -123,6 +123,8 @@ information on what to include when reporting a bug.
|
|||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
- Added `glfwSetWindowBadge` and `glfwSetWindowBadgeString` for displaying window application badge (#2248)
|
||||||
|
- Added `glfwSetWindowProgressIndicator` for displaying progress on the dock or taskbar (#2286,#1183)
|
||||||
- Added `GLFW_PLATFORM` init hint for runtime platform selection (#1958)
|
- Added `GLFW_PLATFORM` init hint for runtime platform selection (#1958)
|
||||||
- Added `GLFW_ANY_PLATFORM`, `GLFW_PLATFORM_WIN32`, `GLFW_PLATFORM_COCOA`,
|
- Added `GLFW_ANY_PLATFORM`, `GLFW_PLATFORM_WIN32`, `GLFW_PLATFORM_COCOA`,
|
||||||
`GLFW_PLATFORM_WAYLAND`, `GLFW_PLATFORM_X11` and `GLFW_PLATFORM_NULL` symbols to
|
`GLFW_PLATFORM_WAYLAND`, `GLFW_PLATFORM_X11` and `GLFW_PLATFORM_NULL` symbols to
|
||||||
|
@ -1159,6 +1159,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.
|
||||||
|
|
||||||
|
|
||||||
|
@subsection 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.
|
||||||
|
|
||||||
|
@code
|
||||||
|
glfwSetWindowProgressIndicator(window, GLFW_PROGRESS_INDICATOR_NORMAL, 0.5);
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
@subsection window_refresh Window damage and refresh
|
@subsection window_refresh Window damage and 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
|
||||||
|
@ -1119,6 +1119,7 @@ extern "C" {
|
|||||||
*/
|
*/
|
||||||
#define GLFW_X11_INSTANCE_NAME 0x00024002
|
#define GLFW_X11_INSTANCE_NAME 0x00024002
|
||||||
#define GLFW_WIN32_KEYBOARD_MENU 0x00025001
|
#define GLFW_WIN32_KEYBOARD_MENU 0x00025001
|
||||||
|
#define GLFW_WIN32_GENERIC_BADGE 0x00025002
|
||||||
/*! @brief Wayland specific
|
/*! @brief Wayland specific
|
||||||
* [window hint](@ref GLFW_WAYLAND_APP_ID_hint).
|
* [window hint](@ref GLFW_WAYLAND_APP_ID_hint).
|
||||||
*
|
*
|
||||||
@ -1277,6 +1278,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
|
||||||
|
|
||||||
@ -3314,6 +3368,106 @@ 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 Sets the dock or taskbar badge for the specified window or the application.
|
||||||
|
*
|
||||||
|
* This function sets the dock or taskbar badge for the specified window
|
||||||
|
* or the application as a whole. Any badge set with this function
|
||||||
|
* overrides the string badge set with @ref glfwSetWindowBadgeString.
|
||||||
|
* If the platform does not support number badges, the string badge
|
||||||
|
* is not overridden.
|
||||||
|
*
|
||||||
|
* @param[in] window The window whose badge to set.
|
||||||
|
* @param[in] count The number to set, or `0` to disable it.
|
||||||
|
*
|
||||||
|
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
|
||||||
|
* GLFW_PLATFORM_ERROR, and @ref GLFW_FEATURE_UNAVAILABLE (see remarks).
|
||||||
|
*
|
||||||
|
* @remark @win32 On Windows Vista and earlier, this function will emit
|
||||||
|
* @ref GLFW_FEATURE_UNAVAILABLE.
|
||||||
|
*
|
||||||
|
* @remark @macos Only the Dock icon may contain a badge. Pass a `NULL`
|
||||||
|
* window handle to set it. Emits @ref GLFW_FEATURE_UNAVAILABLE if a
|
||||||
|
* valid window handle is passed.
|
||||||
|
*
|
||||||
|
* @remark @x11 @wayland @win32 Emits GLFW_FEATURE_UNAVAILABLE if a
|
||||||
|
* `NULL` window handle is passed.
|
||||||
|
*
|
||||||
|
* @thread_safety This function must only be called from the main thread.
|
||||||
|
*
|
||||||
|
* @since Added in version 3.4.
|
||||||
|
*
|
||||||
|
* @ingroup window
|
||||||
|
*/
|
||||||
|
GLFWAPI void glfwSetWindowBadge(GLFWwindow* window, int count);
|
||||||
|
|
||||||
|
/*! @brief Sets the dock or taskbar badge for the specified window or the application.
|
||||||
|
*
|
||||||
|
* This function sets the dock or taskbar badge for the specified window
|
||||||
|
* or the application as a whole. Any string badge set with this function
|
||||||
|
* overrides the number badge set with @ref glfwSetWindowBadge.
|
||||||
|
* If the platform does not support string badges, the number badge
|
||||||
|
* is not overridden.
|
||||||
|
*
|
||||||
|
* @param[in] window The window whose badge to set.
|
||||||
|
* @param[in] string The text to set, or `NULL` to disable it.
|
||||||
|
*
|
||||||
|
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref
|
||||||
|
* GLFW_PLATFORM_ERROR, and @ref GLFW_FEATURE_UNAVAILABLE (see remarks).
|
||||||
|
*
|
||||||
|
* @remark @macos Only the Dock icon may contain a badge. Pass a `NULL`
|
||||||
|
* window handle to set it. Emits @ref GLFW_FEATURE_UNAVAILABLE if a
|
||||||
|
* valid window handle is passed.
|
||||||
|
*
|
||||||
|
* @remark @x11 @wayland @win32 Emits GLFW_FEATURE_UNAVAILABLE.
|
||||||
|
*
|
||||||
|
* @thread_safety This function must only be called from the main thread.
|
||||||
|
*
|
||||||
|
* @since Added in version 3.4.
|
||||||
|
*
|
||||||
|
* @ingroup window
|
||||||
|
*/
|
||||||
|
GLFWAPI void glfwSetWindowBadgeString(GLFWwindow* window, const char* string);
|
||||||
|
|
||||||
/*! @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
|
||||||
|
@ -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
|
||||||
|
@ -528,6 +528,9 @@ GLFWbool _glfwConnectCocoa(int platformID, _GLFWplatform* platform)
|
|||||||
_glfwDestroyWindowCocoa,
|
_glfwDestroyWindowCocoa,
|
||||||
_glfwSetWindowTitleCocoa,
|
_glfwSetWindowTitleCocoa,
|
||||||
_glfwSetWindowIconCocoa,
|
_glfwSetWindowIconCocoa,
|
||||||
|
_glfwSetWindowProgressIndicatorCocoa,
|
||||||
|
_glfwSetWindowBadgeCocoa,
|
||||||
|
_glfwSetWindowBadgeStringCocoa,
|
||||||
_glfwGetWindowPosCocoa,
|
_glfwGetWindowPosCocoa,
|
||||||
_glfwSetWindowPosCocoa,
|
_glfwSetWindowPosCocoa,
|
||||||
_glfwGetWindowSizeCocoa,
|
_glfwGetWindowSizeCocoa,
|
||||||
@ -649,6 +652,12 @@ 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)
|
||||||
{
|
{
|
||||||
CFRelease(_glfw.ns.inputSource);
|
CFRelease(_glfw.ns.inputSource);
|
||||||
|
@ -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,9 @@ 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 _glfwSetWindowBadgeCocoa(_GLFWwindow* window, int count);
|
||||||
|
void _glfwSetWindowBadgeStringCocoa(_GLFWwindow* window, const char* string);
|
||||||
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);
|
||||||
|
@ -197,6 +197,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
|
||||||
@ -992,6 +1069,8 @@ 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;
|
||||||
|
|
||||||
@ -1037,6 +1116,107 @@ 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 _glfwSetWindowBadgeCocoa(_GLFWwindow* window, int count)
|
||||||
|
{
|
||||||
|
if (window != NULL)
|
||||||
|
{
|
||||||
|
_glfwInputError(GLFW_FEATURE_UNAVAILABLE,
|
||||||
|
"Cocoa: Cannot set a badge for a window. Pass NULL to set the Dock badge.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
[NSApp dockTile].badgeLabel = nil;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString* string;
|
||||||
|
|
||||||
|
if (count <= 9999)
|
||||||
|
string = [@(count) stringValue];
|
||||||
|
else
|
||||||
|
string = [[@(9999) stringValue] stringByAppendingString:@"+"];
|
||||||
|
|
||||||
|
[NSApp dockTile].badgeLabel = string;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _glfwSetWindowBadgeStringCocoa(_GLFWwindow* window, const char* string)
|
||||||
|
{
|
||||||
|
if (window != NULL)
|
||||||
|
{
|
||||||
|
_glfwInputError(GLFW_FEATURE_UNAVAILABLE,
|
||||||
|
"Cocoa: Cannot set a badge for a window. Pass NULL to set for the application.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string == NULL)
|
||||||
|
{
|
||||||
|
[NSApp dockTile].badgeLabel = nil;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString* nsString = [NSString stringWithCString:string
|
||||||
|
encoding:[NSString defaultCStringEncoding]];
|
||||||
|
|
||||||
|
[NSApp dockTile].badgeLabel = nsString;
|
||||||
|
}
|
||||||
|
|
||||||
void _glfwGetWindowPosCocoa(_GLFWwindow* window, int* xpos, int* ypos)
|
void _glfwGetWindowPosCocoa(_GLFWwindow* window, int* xpos, int* ypos)
|
||||||
{
|
{
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
|
@ -418,6 +418,7 @@ struct _GLFWwndconfig
|
|||||||
} x11;
|
} x11;
|
||||||
struct {
|
struct {
|
||||||
GLFWbool keymenu;
|
GLFWbool keymenu;
|
||||||
|
GLFWbool genericBadge;
|
||||||
} win32;
|
} win32;
|
||||||
struct {
|
struct {
|
||||||
char appId[256];
|
char appId[256];
|
||||||
@ -709,6 +710,9 @@ 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 (*setWindowBadge)(_GLFWwindow*,int);
|
||||||
|
void (*setWindowBadgeString)(_GLFWwindow*,const char* string);
|
||||||
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*);
|
||||||
@ -872,6 +876,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
|
||||||
|
@ -72,6 +72,9 @@ GLFWbool _glfwConnectNull(int platformID, _GLFWplatform* platform)
|
|||||||
_glfwDestroyWindowNull,
|
_glfwDestroyWindowNull,
|
||||||
_glfwSetWindowTitleNull,
|
_glfwSetWindowTitleNull,
|
||||||
_glfwSetWindowIconNull,
|
_glfwSetWindowIconNull,
|
||||||
|
_glfwSetWindowProgressIndicatorNull,
|
||||||
|
_glfwSetWindowBadgeNull,
|
||||||
|
_glfwSetWindowBadgeStringNull,
|
||||||
_glfwGetWindowPosNull,
|
_glfwGetWindowPosNull,
|
||||||
_glfwSetWindowPosNull,
|
_glfwSetWindowPosNull,
|
||||||
_glfwGetWindowSizeNull,
|
_glfwGetWindowSizeNull,
|
||||||
|
@ -213,6 +213,9 @@ 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 _glfwSetWindowBadgeNull(_GLFWwindow* window, int count);
|
||||||
|
void _glfwSetWindowBadgeStringNull(_GLFWwindow* window, const char* string);
|
||||||
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);
|
||||||
|
@ -185,6 +185,18 @@ void _glfwSetWindowIconNull(_GLFWwindow* window, int count, const GLFWimage* ima
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _glfwSetWindowProgressIndicatorNull(_GLFWwindow* window, int progressState, double value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void _glfwSetWindowBadgeNull(_GLFWwindow* window, int count)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void _glfwSetWindowBadgeStringNull(_GLFWwindow* window, const char* string)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void _glfwSetWindowMonitorNull(_GLFWwindow* window,
|
void _glfwSetWindowMonitorNull(_GLFWwindow* window,
|
||||||
_GLFWmonitor* monitor,
|
_GLFWmonitor* monitor,
|
||||||
int xpos, int ypos,
|
int xpos, int ypos,
|
||||||
|
@ -178,6 +178,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)
|
||||||
@ -191,6 +192,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
|
||||||
|
518
src/posix_dbus.c
Normal file
518
src/posix_dbus.c
Normal file
@ -0,0 +1,518 @@
|
|||||||
|
//========================================================================
|
||||||
|
// 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>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _glfwUpdateBadgeDBusPOSIX(dbus_bool_t badgeVisible, int badgeCount)
|
||||||
|
{
|
||||||
|
struct DBusMessage* msg = NULL;
|
||||||
|
|
||||||
|
if(!_glfw.dbus.handle || !_glfw.dbus.connection || !_glfw.dbus.desktopFilePath || !_glfw.dbus.signalName)
|
||||||
|
return;
|
||||||
|
|
||||||
|
long long badgeCountLL = badgeCount;
|
||||||
|
|
||||||
|
//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 count visible property
|
||||||
|
const char* countVisibleStr = "count-visible";
|
||||||
|
_glfwAppendDictDataDBusPOSIX(&sub1, DBUS_TYPE_STRING, &countVisibleStr, DBUS_TYPE_BOOLEAN, &badgeVisible);
|
||||||
|
|
||||||
|
//Set count value property
|
||||||
|
const char* countValueStr = "count";
|
||||||
|
_glfwAppendDictDataDBusPOSIX(&sub1, DBUS_TYPE_STRING, &countValueStr, DBUS_TYPE_INT64, &badgeCountLL);
|
||||||
|
|
||||||
|
_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;
|
||||||
|
|
||||||
|
if(!_glfwOpenContainerDBusPOSIX(&keyIterator, DBUS_TYPE_VARIANT, (const char*)&valueType, &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;
|
||||||
|
}
|
159
src/posix_dbus.h
Normal file
159
src/posix_dbus.h
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
//========================================================================
|
||||||
|
// 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'
|
||||||
|
|
||||||
|
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);
|
||||||
|
void _glfwUpdateBadgeDBusPOSIX(dbus_bool_t badgeVisible, int badgeCount);
|
||||||
|
|
||||||
|
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);
|
@ -675,6 +675,9 @@ GLFWbool _glfwConnectWin32(int platformID, _GLFWplatform* platform)
|
|||||||
_glfwDestroyWindowWin32,
|
_glfwDestroyWindowWin32,
|
||||||
_glfwSetWindowTitleWin32,
|
_glfwSetWindowTitleWin32,
|
||||||
_glfwSetWindowIconWin32,
|
_glfwSetWindowIconWin32,
|
||||||
|
_glfwSetWindowProgressIndicatorWin32,
|
||||||
|
_glfwSetWindowBadgeWin32,
|
||||||
|
_glfwSetWindowBadgeStringWin32,
|
||||||
_glfwGetWindowPosWin32,
|
_glfwGetWindowPosWin32,
|
||||||
_glfwSetWindowPosWin32,
|
_glfwSetWindowPosWin32,
|
||||||
_glfwGetWindowSizeWin32,
|
_glfwGetWindowSizeWin32,
|
||||||
|
@ -366,6 +366,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
|
||||||
//
|
//
|
||||||
@ -424,6 +497,7 @@ typedef struct _GLFWwindowWin32
|
|||||||
GLFWbool transparent;
|
GLFWbool transparent;
|
||||||
GLFWbool scaleToMonitor;
|
GLFWbool scaleToMonitor;
|
||||||
GLFWbool keymenu;
|
GLFWbool keymenu;
|
||||||
|
GLFWbool genericBadge;
|
||||||
|
|
||||||
// Cached size used to filter out duplicate events
|
// Cached size used to filter out duplicate events
|
||||||
int width, height;
|
int width, height;
|
||||||
@ -432,6 +506,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
|
||||||
@ -526,7 +603,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);
|
||||||
@ -547,6 +623,9 @@ 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 _glfwSetWindowBadgeWin32(_GLFWwindow* window, int count);
|
||||||
|
void _glfwSetWindowBadgeStringWin32(_GLFWwindow* window, const char* string);
|
||||||
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);
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <windowsx.h>
|
#include <windowsx.h>
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
// Returns the window style for the specified window
|
// Returns the window style for the specified window
|
||||||
//
|
//
|
||||||
@ -1267,6 +1268,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1386,10 +1399,15 @@ static int createNativeWindow(_GLFWwindow* window,
|
|||||||
WM_COPYDATA, MSGFLT_ALLOW, NULL);
|
WM_COPYDATA, MSGFLT_ALLOW, NULL);
|
||||||
ChangeWindowMessageFilterEx(window->win32.handle,
|
ChangeWindowMessageFilterEx(window->win32.handle,
|
||||||
WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL);
|
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.genericBadge = wndconfig->win32.genericBadge;
|
||||||
|
|
||||||
if (!window->monitor)
|
if (!window->monitor)
|
||||||
{
|
{
|
||||||
@ -1538,6 +1556,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");
|
||||||
@ -1600,6 +1621,492 @@ 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(!IsWindows7OrGreater())
|
||||||
|
{
|
||||||
|
_glfwInputError(GLFW_FEATURE_UNAVAILABLE, "Win32: Taskbar progress is only supported on Windows 7 and newer");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
HDC hdc;
|
||||||
|
HDC hdcMem;
|
||||||
|
HDC hdcMemMask;
|
||||||
|
|
||||||
|
HBITMAP hBitmap;
|
||||||
|
HBITMAP hBitmapMask;
|
||||||
|
HBITMAP hOldBitmap;
|
||||||
|
HBITMAP hOldBitmapMask;
|
||||||
|
|
||||||
|
HBRUSH hForegroundBrush;
|
||||||
|
HBRUSH hBackgroundBrush;
|
||||||
|
HBRUSH hOldBrush;
|
||||||
|
|
||||||
|
HFONT hFont;
|
||||||
|
HFONT hOldFont;
|
||||||
|
|
||||||
|
COLORREF hOldColor;
|
||||||
|
|
||||||
|
int oldBkMode;
|
||||||
|
|
||||||
|
UINT oldTextAlign;
|
||||||
|
} BadgeData;
|
||||||
|
|
||||||
|
static void CleanupBadgeData(HWND hWnd, BadgeData* data)
|
||||||
|
{
|
||||||
|
if(!data)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(data->oldTextAlign != GDI_ERROR)
|
||||||
|
SetTextAlign(data->hdcMem, data->oldTextAlign);
|
||||||
|
|
||||||
|
if(data->oldBkMode)
|
||||||
|
SetBkMode(data->hdcMem, data->oldBkMode);
|
||||||
|
|
||||||
|
if(data->hOldColor != CLR_INVALID)
|
||||||
|
SetTextColor(data->hdcMem, data->hOldColor);
|
||||||
|
|
||||||
|
if(data->hFont)
|
||||||
|
DeleteObject(data->hFont);
|
||||||
|
if(data->hOldFont)
|
||||||
|
SelectObject(data->hdcMem, data->hOldFont);
|
||||||
|
|
||||||
|
if (data->hForegroundBrush)
|
||||||
|
DeleteObject(data->hForegroundBrush);
|
||||||
|
if (data->hBackgroundBrush)
|
||||||
|
DeleteObject(data->hBackgroundBrush);
|
||||||
|
|
||||||
|
if(data->hOldBrush)
|
||||||
|
SelectObject(data->hdcMem, data->hOldBrush);
|
||||||
|
|
||||||
|
if(data->hOldBitmap)
|
||||||
|
SelectObject(data->hdcMem, data->hOldBitmap);
|
||||||
|
if(data->hOldBitmapMask)
|
||||||
|
SelectObject(data->hdcMem, data->hOldBitmapMask);
|
||||||
|
|
||||||
|
if(data->hBitmap)
|
||||||
|
DeleteObject(data->hBitmap);
|
||||||
|
if(data->hBitmapMask)
|
||||||
|
DeleteObject(data->hBitmapMask);
|
||||||
|
|
||||||
|
if(data->hdcMem)
|
||||||
|
DeleteDC(data->hdcMem);
|
||||||
|
if(data->hdcMemMask)
|
||||||
|
DeleteDC(data->hdcMemMask);
|
||||||
|
|
||||||
|
if(data->hdc)
|
||||||
|
ReleaseDC(hWnd, data->hdc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static HICON GenerateTextBadgeIcon(HWND hWnd, WCHAR* text)
|
||||||
|
{
|
||||||
|
BadgeData badgeData;
|
||||||
|
|
||||||
|
void* bits = NULL;
|
||||||
|
DWORD* pixels = NULL;
|
||||||
|
ICONINFO iconInfo;
|
||||||
|
int width = 16, height = 16;
|
||||||
|
int fontSize = 16, weight = FW_REGULAR;
|
||||||
|
RECT contentRect = { 0, 0, width, height };
|
||||||
|
HICON hIcon = NULL;
|
||||||
|
BITMAPINFO bmi = {0};
|
||||||
|
int x = 0, y = 0;
|
||||||
|
|
||||||
|
memset(&badgeData, 0, sizeof(BadgeData));
|
||||||
|
|
||||||
|
if (!text)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||||
|
bmi.bmiHeader.biWidth = width;
|
||||||
|
bmi.bmiHeader.biHeight = height;
|
||||||
|
bmi.bmiHeader.biPlanes = 1;
|
||||||
|
bmi.bmiHeader.biBitCount = 32;
|
||||||
|
bmi.bmiHeader.biCompression = BI_RGB;
|
||||||
|
|
||||||
|
badgeData.hdc = GetDC(hWnd);
|
||||||
|
if(!badgeData.hdc)
|
||||||
|
{
|
||||||
|
CleanupBadgeData(hWnd, &badgeData);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
badgeData.hdcMem = CreateCompatibleDC(badgeData.hdc);
|
||||||
|
if(!badgeData.hdcMem)
|
||||||
|
{
|
||||||
|
CleanupBadgeData(hWnd, &badgeData);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
badgeData.hdcMemMask = CreateCompatibleDC(badgeData.hdc);
|
||||||
|
if(!badgeData.hdcMemMask)
|
||||||
|
{
|
||||||
|
CleanupBadgeData(hWnd, &badgeData);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
badgeData.hBitmap = CreateDIBSection(badgeData.hdc, &bmi, DIB_RGB_COLORS, &bits, NULL, 0);
|
||||||
|
if(!badgeData.hBitmap)
|
||||||
|
{
|
||||||
|
CleanupBadgeData(hWnd, &badgeData);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
pixels = (DWORD*)bits;
|
||||||
|
|
||||||
|
badgeData.hBitmapMask = CreateCompatibleBitmap(badgeData.hdc, width, height);
|
||||||
|
if(!badgeData.hBitmapMask)
|
||||||
|
{
|
||||||
|
CleanupBadgeData(hWnd, &badgeData);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
badgeData.hOldBitmap = (HBITMAP)SelectObject(badgeData.hdcMem, badgeData.hBitmap);
|
||||||
|
badgeData.hOldBitmapMask = (HBITMAP)SelectObject(badgeData.hdcMemMask, badgeData.hBitmapMask);
|
||||||
|
|
||||||
|
if(BitBlt(badgeData.hdcMemMask, 0, 0, width, height, badgeData.hdcMem, 0, 0, SRCCOPY) == FALSE)
|
||||||
|
{
|
||||||
|
CleanupBadgeData(hWnd, &badgeData);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
badgeData.hBackgroundBrush = CreateSolidBrush(RGB(0x26, 0x25, 0x2D));
|
||||||
|
if(!badgeData.hBackgroundBrush)
|
||||||
|
{
|
||||||
|
CleanupBadgeData(hWnd, &badgeData);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
badgeData.hOldBrush = (HBRUSH)SelectObject(badgeData.hdcMem, badgeData.hBackgroundBrush);
|
||||||
|
|
||||||
|
if(Ellipse(badgeData.hdcMem, 0, 0, width + 1, height + 1) == FALSE) //17x17 gives a more fancy ellipse
|
||||||
|
{
|
||||||
|
CleanupBadgeData(hWnd, &badgeData);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Adjust font size depending on digits to display
|
||||||
|
if (lstrlen(text) > 2)
|
||||||
|
{
|
||||||
|
fontSize = 10;
|
||||||
|
weight = FW_LIGHT;
|
||||||
|
}
|
||||||
|
else if (lstrlen(text) > 1)
|
||||||
|
fontSize = 14;
|
||||||
|
|
||||||
|
//Create and set font
|
||||||
|
badgeData.hFont = CreateFont(fontSize, 0, 0, 0, weight, FALSE, FALSE, FALSE, 0,
|
||||||
|
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY,
|
||||||
|
DEFAULT_PITCH | FF_DONTCARE, TEXT("Segoe UI"));
|
||||||
|
if(!badgeData.hFont)
|
||||||
|
{
|
||||||
|
CleanupBadgeData(hWnd, &badgeData);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
badgeData.hOldFont = (HFONT)SelectObject(badgeData.hdcMem, badgeData.hFont);
|
||||||
|
|
||||||
|
//Draw text (center aligned)
|
||||||
|
badgeData.hOldColor = SetTextColor(badgeData.hdcMem, RGB(255, 255, 255)); //Use white text color
|
||||||
|
if(badgeData.hOldColor == CLR_INVALID)
|
||||||
|
{
|
||||||
|
CleanupBadgeData(hWnd, &badgeData);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
badgeData.oldBkMode = SetBkMode(badgeData.hdcMem, TRANSPARENT); //Make font background transparent
|
||||||
|
if(!badgeData.oldBkMode)
|
||||||
|
{
|
||||||
|
CleanupBadgeData(hWnd, &badgeData);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
badgeData.oldTextAlign = SetTextAlign(badgeData.hdcMem, TA_LEFT | TA_TOP | TA_NOUPDATECP);
|
||||||
|
if(badgeData.oldTextAlign == GDI_ERROR)
|
||||||
|
{
|
||||||
|
CleanupBadgeData(hWnd, &badgeData);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!DrawText(badgeData.hdcMem, text, lstrlen(text), &contentRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE))
|
||||||
|
{
|
||||||
|
CleanupBadgeData(hWnd, &badgeData);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Transparency
|
||||||
|
for (y = 0; y < height; ++y)
|
||||||
|
{
|
||||||
|
for (x = 0; x < width; ++x)
|
||||||
|
{
|
||||||
|
const DWORD pixel = pixels[y * width + x];
|
||||||
|
if (pixel == 0x0026252Du || pixel == 0x00FFFFFFu) //Pixel is text or ellipsis
|
||||||
|
pixels[y * width + x] |= 0xFF000000u; //Set opaque
|
||||||
|
else
|
||||||
|
pixels[y * width + x] &= 0xFF000000u; //Set fully transparent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectObject(badgeData.hdcMem, badgeData.hOldBitmap);
|
||||||
|
badgeData.hOldBitmap = NULL;
|
||||||
|
SelectObject(badgeData.hdcMemMask, badgeData.hOldBitmapMask);
|
||||||
|
badgeData.hOldBitmapMask = NULL;
|
||||||
|
|
||||||
|
//Generate icon from bitmap
|
||||||
|
iconInfo.fIcon = TRUE;
|
||||||
|
iconInfo.xHotspot = 0;
|
||||||
|
iconInfo.yHotspot = 0;
|
||||||
|
iconInfo.hbmMask = badgeData.hBitmapMask;
|
||||||
|
iconInfo.hbmColor = badgeData.hBitmap;
|
||||||
|
hIcon = CreateIconIndirect(&iconInfo);
|
||||||
|
|
||||||
|
CleanupBadgeData(hWnd, &badgeData);
|
||||||
|
|
||||||
|
return hIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HICON GenerateGenericBadgeIcon(HWND hWnd)
|
||||||
|
{
|
||||||
|
BadgeData badgeData;
|
||||||
|
|
||||||
|
void* bits = NULL;
|
||||||
|
DWORD* pixels = NULL;
|
||||||
|
ICONINFO iconInfo;
|
||||||
|
int width = 32, height = 32;
|
||||||
|
HICON hIcon = NULL;
|
||||||
|
BITMAPINFO bmi;
|
||||||
|
int x = 0, y = 0;
|
||||||
|
|
||||||
|
memset(&badgeData, 0, sizeof(BadgeData));
|
||||||
|
|
||||||
|
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||||
|
bmi.bmiHeader.biWidth = width;
|
||||||
|
bmi.bmiHeader.biHeight = height;
|
||||||
|
bmi.bmiHeader.biPlanes = 1;
|
||||||
|
bmi.bmiHeader.biBitCount = 32;
|
||||||
|
bmi.bmiHeader.biCompression = BI_RGB;
|
||||||
|
|
||||||
|
badgeData.hdc = GetDC(hWnd);
|
||||||
|
if (!badgeData.hdc)
|
||||||
|
{
|
||||||
|
CleanupBadgeData(hWnd, &badgeData);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
badgeData.hdcMem = CreateCompatibleDC(badgeData.hdc);
|
||||||
|
if (!badgeData.hdcMem)
|
||||||
|
{
|
||||||
|
CleanupBadgeData(hWnd, &badgeData);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
badgeData.hdcMemMask = CreateCompatibleDC(badgeData.hdc);
|
||||||
|
if (!badgeData.hdcMemMask)
|
||||||
|
{
|
||||||
|
CleanupBadgeData(hWnd, &badgeData);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
badgeData.hBitmap = CreateDIBSection(badgeData.hdc, &bmi, DIB_RGB_COLORS, &bits, NULL, 0);
|
||||||
|
if (!badgeData.hBitmap)
|
||||||
|
{
|
||||||
|
CleanupBadgeData(hWnd, &badgeData);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
pixels = (DWORD*)bits;
|
||||||
|
|
||||||
|
badgeData.hBitmapMask = CreateCompatibleBitmap(badgeData.hdc, width, height);
|
||||||
|
if (!badgeData.hBitmapMask)
|
||||||
|
{
|
||||||
|
CleanupBadgeData(hWnd, &badgeData);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
badgeData.hOldBitmap = (HBITMAP)SelectObject(badgeData.hdcMem, badgeData.hBitmap);
|
||||||
|
badgeData.hOldBitmapMask = (HBITMAP)SelectObject(badgeData.hdcMemMask, badgeData.hBitmapMask);
|
||||||
|
|
||||||
|
if (BitBlt(badgeData.hdcMemMask, 0, 0, width, height, badgeData.hdcMem, 0, 0, SRCCOPY) == FALSE)
|
||||||
|
{
|
||||||
|
CleanupBadgeData(hWnd, &badgeData);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
badgeData.hBackgroundBrush = CreateSolidBrush(RGB(0xEB, 0x5A, 0x5E));
|
||||||
|
if (!badgeData.hBackgroundBrush)
|
||||||
|
{
|
||||||
|
CleanupBadgeData(hWnd, &badgeData);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
badgeData.hOldBrush = (HBRUSH)SelectObject(badgeData.hdcMem, badgeData.hBackgroundBrush);
|
||||||
|
|
||||||
|
if (Ellipse(badgeData.hdcMem, 0, 0, width + 1, height + 1) == FALSE) //17x17 gives a more fancy ellipse
|
||||||
|
{
|
||||||
|
CleanupBadgeData(hWnd, &badgeData);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
badgeData.hForegroundBrush = CreateSolidBrush(RGB(0xFF, 0xFF, 0xFF));
|
||||||
|
if (!badgeData.hForegroundBrush)
|
||||||
|
{
|
||||||
|
CleanupBadgeData(hWnd, &badgeData);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectObject(badgeData.hdcMem, badgeData.hForegroundBrush);
|
||||||
|
|
||||||
|
if (Ellipse(badgeData.hdcMem, 9, 9, (width - 8), (height - 8)) == FALSE)
|
||||||
|
{
|
||||||
|
CleanupBadgeData(hWnd, &badgeData);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectObject(badgeData.hdcMem, badgeData.hOldBitmap);
|
||||||
|
badgeData.hOldBitmap = NULL;
|
||||||
|
SelectObject(badgeData.hdcMemMask, badgeData.hOldBitmapMask);
|
||||||
|
badgeData.hOldBitmapMask = NULL;
|
||||||
|
|
||||||
|
//Transparency
|
||||||
|
for (y = 0; y < height; ++y)
|
||||||
|
{
|
||||||
|
for (x = 0; x < width; ++x)
|
||||||
|
{
|
||||||
|
const DWORD pixel = pixels[y * width + x];
|
||||||
|
if (pixel >= 0x00010101u) //Pixel is ellipsis
|
||||||
|
pixels[y * width + x] |= 0xFF000000u; //Set opaque
|
||||||
|
else
|
||||||
|
pixels[y * width + x] &= 0xFF000000u; //Set fully transparent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Generate icon from bitmap
|
||||||
|
iconInfo.fIcon = TRUE;
|
||||||
|
iconInfo.xHotspot = 0;
|
||||||
|
iconInfo.yHotspot = 0;
|
||||||
|
iconInfo.hbmMask = badgeData.hBitmapMask;
|
||||||
|
iconInfo.hbmColor = badgeData.hBitmap;
|
||||||
|
hIcon = CreateIconIndirect(&iconInfo);
|
||||||
|
|
||||||
|
CleanupBadgeData(hWnd, &badgeData);
|
||||||
|
|
||||||
|
return hIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _glfwSetWindowBadgeWin32(_GLFWwindow* window, int count)
|
||||||
|
{
|
||||||
|
HRESULT res = S_OK;
|
||||||
|
HICON icon = NULL;
|
||||||
|
char countStr[4];
|
||||||
|
WCHAR* countWStr = NULL;
|
||||||
|
|
||||||
|
if (window == NULL)
|
||||||
|
{
|
||||||
|
_glfwInputError(GLFW_FEATURE_UNAVAILABLE, "Win32: Taskbar badge requires a valid window handle");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsWindows7OrGreater())
|
||||||
|
{
|
||||||
|
_glfwInputError(GLFW_FEATURE_UNAVAILABLE, "Win32: Taskbar badge is only supported on Windows 7 and newer");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!window->win32.taskbarList)
|
||||||
|
{
|
||||||
|
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to set taskbar badge count");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
count = _glfw_min(count, 999);
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
if (window->win32.genericBadge)
|
||||||
|
icon = GenerateGenericBadgeIcon(window->win32.handle);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//Convert count to string (its guaranteed to be at max 3 digits)
|
||||||
|
memset(countStr, 0, 4 * sizeof(char));
|
||||||
|
sprintf(countStr, "%d", count);
|
||||||
|
countWStr = _glfwCreateWideStringFromUTF8Win32(countStr);
|
||||||
|
if (!countWStr)
|
||||||
|
{
|
||||||
|
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to set taskbar badge count");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
icon = GenerateTextBadgeIcon(window->win32.handle, countWStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!icon)
|
||||||
|
{
|
||||||
|
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to set taskbar badge count");
|
||||||
|
_glfw_free(countWStr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res = window->win32.taskbarList->lpVtbl->SetOverlayIcon(window->win32.taskbarList, window->win32.handle, icon, countWStr ? countWStr : TEXT(""));
|
||||||
|
|
||||||
|
if (countWStr)
|
||||||
|
_glfw_free(countWStr);
|
||||||
|
|
||||||
|
if(icon)
|
||||||
|
DestroyIcon(icon);
|
||||||
|
|
||||||
|
if (res != S_OK)
|
||||||
|
{
|
||||||
|
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to set taskbar badge count");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _glfwSetWindowBadgeStringWin32(_GLFWwindow* window, const char* string)
|
||||||
|
{
|
||||||
|
_glfwInputError(GLFW_FEATURE_UNAVAILABLE,
|
||||||
|
"Win32: Unable to set a string badge. Only integer badges are supported");
|
||||||
|
//In reality you can display a string but it could only be 3 maybe 4 characters long till it becomes an unreadable mess.
|
||||||
|
}
|
||||||
|
|
||||||
void _glfwGetWindowPosWin32(_GLFWwindow* window, int* xpos, int* ypos)
|
void _glfwGetWindowPosWin32(_GLFWwindow* window, int* xpos, int* ypos)
|
||||||
{
|
{
|
||||||
POINT pos = { 0, 0 };
|
POINT pos = { 0, 0 };
|
||||||
|
52
src/window.c
52
src/window.c
@ -380,6 +380,9 @@ GLFWAPI void glfwWindowHint(int hint, int value)
|
|||||||
case GLFW_WIN32_KEYBOARD_MENU:
|
case GLFW_WIN32_KEYBOARD_MENU:
|
||||||
_glfw.hints.window.win32.keymenu = value ? GLFW_TRUE : GLFW_FALSE;
|
_glfw.hints.window.win32.keymenu = value ? GLFW_TRUE : GLFW_FALSE;
|
||||||
return;
|
return;
|
||||||
|
case GLFW_WIN32_GENERIC_BADGE:
|
||||||
|
_glfw.hints.window.win32.genericBadge = value ? GLFW_TRUE : GLFW_FALSE;
|
||||||
|
return;
|
||||||
case GLFW_COCOA_GRAPHICS_SWITCHING:
|
case GLFW_COCOA_GRAPHICS_SWITCHING:
|
||||||
_glfw.hints.context.nsgl.offline = value ? GLFW_TRUE : GLFW_FALSE;
|
_glfw.hints.context.nsgl.offline = value ? GLFW_TRUE : GLFW_FALSE;
|
||||||
return;
|
return;
|
||||||
@ -556,6 +559,55 @@ 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 glfwSetWindowBadge(GLFWwindow* handle, int count)
|
||||||
|
{
|
||||||
|
_GLFWwindow* window = (_GLFWwindow*)handle;
|
||||||
|
|
||||||
|
_GLFW_REQUIRE_INIT();
|
||||||
|
|
||||||
|
if (count < 0)
|
||||||
|
{
|
||||||
|
_glfwInputError(GLFW_INVALID_VALUE, "Invalid badge count %d", count);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_glfw.platform.setWindowBadge(window, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
GLFWAPI void glfwSetWindowBadgeString(GLFWwindow* handle, const char* string)
|
||||||
|
{
|
||||||
|
_GLFWwindow* window = (_GLFWwindow*)handle;
|
||||||
|
|
||||||
|
_GLFW_REQUIRE_INIT();
|
||||||
|
|
||||||
|
_glfw.platform.setWindowBadgeString(window, string);
|
||||||
|
}
|
||||||
|
|
||||||
GLFWAPI void glfwGetWindowPos(GLFWwindow* handle, int* xpos, int* ypos)
|
GLFWAPI void glfwGetWindowPos(GLFWwindow* handle, int* xpos, int* ypos)
|
||||||
{
|
{
|
||||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||||
|
@ -446,6 +446,9 @@ GLFWbool _glfwConnectWayland(int platformID, _GLFWplatform* platform)
|
|||||||
_glfwDestroyWindowWayland,
|
_glfwDestroyWindowWayland,
|
||||||
_glfwSetWindowTitleWayland,
|
_glfwSetWindowTitleWayland,
|
||||||
_glfwSetWindowIconWayland,
|
_glfwSetWindowIconWayland,
|
||||||
|
_glfwSetWindowProgressIndicatorWayland,
|
||||||
|
_glfwSetWindowBadgeWayland,
|
||||||
|
_glfwSetWindowBadgeStringWayland,
|
||||||
_glfwGetWindowPosWayland,
|
_glfwGetWindowPosWayland,
|
||||||
_glfwSetWindowPosWayland,
|
_glfwSetWindowPosWayland,
|
||||||
_glfwGetWindowSizeWayland,
|
_glfwGetWindowSizeWayland,
|
||||||
@ -532,6 +535,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;
|
||||||
@ -943,6 +948,8 @@ void _glfwTerminateWayland(void)
|
|||||||
close(_glfw.wl.cursorTimerfd);
|
close(_glfw.wl.cursorTimerfd);
|
||||||
|
|
||||||
_glfw_free(_glfw.wl.clipboardString);
|
_glfw_free(_glfw.wl.clipboardString);
|
||||||
|
|
||||||
|
_glfwTerminateDBusPOSIX();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // _GLFW_WAYLAND
|
#endif // _GLFW_WAYLAND
|
||||||
|
@ -613,6 +613,9 @@ 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 _glfwSetWindowBadgeWayland(_GLFWwindow* window, int count);
|
||||||
|
void _glfwSetWindowBadgeStringWayland(_GLFWwindow* window, const char* string);
|
||||||
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);
|
||||||
|
@ -2052,6 +2052,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2113,6 +2121,35 @@ 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 _glfwSetWindowBadgeWayland(_GLFWwindow* window, int count)
|
||||||
|
{
|
||||||
|
if (window != NULL)
|
||||||
|
{
|
||||||
|
_glfwInputError(GLFW_FEATURE_UNAVAILABLE,
|
||||||
|
"Wayland: Cannot set a badge for a window. Pass NULL to set the application's shared badge.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dbus_bool_t badgeVisible = (count > 0);
|
||||||
|
|
||||||
|
_glfwUpdateBadgeDBusPOSIX(badgeVisible, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _glfwSetWindowBadgeStringWayland(_GLFWwindow* window, const char* string)
|
||||||
|
{
|
||||||
|
_glfwInputError(GLFW_FEATURE_UNAVAILABLE,
|
||||||
|
"Wayland: Unable to set a string badge. Only integer badges are supported.");
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
|
@ -1207,6 +1207,9 @@ GLFWbool _glfwConnectX11(int platformID, _GLFWplatform* platform)
|
|||||||
_glfwDestroyWindowX11,
|
_glfwDestroyWindowX11,
|
||||||
_glfwSetWindowTitleX11,
|
_glfwSetWindowTitleX11,
|
||||||
_glfwSetWindowIconX11,
|
_glfwSetWindowIconX11,
|
||||||
|
_glfwSetWindowProgressIndicatorX11,
|
||||||
|
_glfwSetWindowBadgeX11,
|
||||||
|
_glfwSetWindowBadgeStringX11,
|
||||||
_glfwGetWindowPosX11,
|
_glfwGetWindowPosX11,
|
||||||
_glfwSetWindowPosX11,
|
_glfwSetWindowPosX11,
|
||||||
_glfwGetWindowSizeX11,
|
_glfwGetWindowSizeX11,
|
||||||
@ -1319,6 +1322,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)
|
||||||
@ -1650,6 +1655,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
|
||||||
|
@ -905,6 +905,9 @@ 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 _glfwSetWindowBadgeX11(_GLFWwindow* window, int count);
|
||||||
|
void _glfwSetWindowBadgeStringX11(_GLFWwindow* window, const char* string);
|
||||||
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);
|
||||||
|
@ -2038,6 +2038,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;
|
||||||
}
|
}
|
||||||
@ -2150,6 +2158,35 @@ 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 _glfwSetWindowBadgeX11(_GLFWwindow* window, int count)
|
||||||
|
{
|
||||||
|
if (window != NULL)
|
||||||
|
{
|
||||||
|
_glfwInputError(GLFW_FEATURE_UNAVAILABLE,
|
||||||
|
"X11: Cannot set a badge for a window. Pass NULL to set the application's shared badge.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dbus_bool_t badgeVisible = (count > 0);
|
||||||
|
|
||||||
|
_glfwUpdateBadgeDBusPOSIX(badgeVisible, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _glfwSetWindowBadgeStringX11(_GLFWwindow* window, const char* string)
|
||||||
|
{
|
||||||
|
_glfwInputError(GLFW_FEATURE_UNAVAILABLE,
|
||||||
|
"X11: Unable to set a string badge. Only integer badges are supported.");
|
||||||
|
}
|
||||||
|
|
||||||
void _glfwGetWindowPosX11(_GLFWwindow* window, int* xpos, int* ypos)
|
void _glfwGetWindowPosX11(_GLFWwindow* window, int* xpos, int* ypos)
|
||||||
{
|
{
|
||||||
Window dummy;
|
Window dummy;
|
||||||
|
@ -71,7 +71,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, 600, "Window Features", NULL, NULL);
|
GLFWwindow* window = glfwCreateWindow(600, 800, "Window Features", NULL, NULL);
|
||||||
if (!window)
|
if (!window)
|
||||||
{
|
{
|
||||||
glfwTerminate();
|
glfwTerminate();
|
||||||
@ -411,6 +411,45 @@ 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_layout_row_dynamic(nk, 30, 1);
|
||||||
|
|
||||||
|
nk_label(nk, "Badge", NK_TEXT_CENTERED);
|
||||||
|
|
||||||
|
static int badgeCount = 0;
|
||||||
|
nk_layout_row_begin(nk, NK_DYNAMIC, 30, 3);
|
||||||
|
nk_layout_row_push(nk, 1.0f / 3.f);
|
||||||
|
nk_labelf(nk, NK_TEXT_LEFT, "Badge count: %d", badgeCount);
|
||||||
|
nk_layout_row_push(nk, 2.f / 3.f);
|
||||||
|
if (nk_slider_int(nk, 0, &badgeCount, 10000, 1))
|
||||||
|
{
|
||||||
|
glfwSetWindowBadge(window, badgeCount);
|
||||||
|
glfwSetWindowBadge(NULL, badgeCount);
|
||||||
|
}
|
||||||
|
nk_layout_row_end(nk);
|
||||||
}
|
}
|
||||||
nk_end(nk);
|
nk_end(nk);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user