Add glfwSetWindowIcon

Adds support for setting window icons programmatically on platforms
where this makes sense.

Fixes #453.
Closes #467.
This commit is contained in:
Camilla Berglund 2016-03-07 14:55:30 +01:00
parent 793eef1d0a
commit b823f7151e
14 changed files with 299 additions and 55 deletions

View File

@ -84,6 +84,7 @@ does not find Doxygen, the documentation will not be generated.
- Added `glfwGetKeyName` for querying the layout-specific name of printable - Added `glfwGetKeyName` for querying the layout-specific name of printable
keys keys
- Added `glfwWaitEventsTimeout` for waiting for events for a set amount of time - Added `glfwWaitEventsTimeout` for waiting for events for a set amount of time
- Added `glfwSetWindowIcon` for setting the icon of a window
- Added `glfwGetTimerValue` and `glfwGetTimerFrequency` for raw timer access - Added `glfwGetTimerValue` and `glfwGetTimerFrequency` for raw timer access
- Added `GLFWuint64` for platform-independent 64-bit unsigned values - Added `GLFWuint64` for platform-independent 64-bit unsigned values
- Added `GLFW_NO_API` for creating window without contexts - Added `GLFW_NO_API` for creating window without contexts
@ -221,6 +222,7 @@ skills.
- Peoro - Peoro
- Braden Pellett - Braden Pellett
- Arturo J. Pérez - Arturo J. Pérez
- Orson Peters
- Emmanuel Gil Peyrot - Emmanuel Gil Peyrot
- Cyril Pichard - Cyril Pichard
- Pieroman - Pieroman
@ -246,6 +248,7 @@ skills.
- TTK-Bandit - TTK-Bandit
- Sergey Tikhomirov - Sergey Tikhomirov
- A. Tombs - A. Tombs
- Ioannis Tsakpinis
- Samuli Tuomola - Samuli Tuomola
- urraka - urraka
- Jari Vetoniemi - Jari Vetoniemi

View File

@ -32,6 +32,11 @@ GLFW now supports window maximization with @ref glfwMaximizeWindow and the
[GLFW_MAXIMIZED](@ref window_attribs_wnd) window hint and attribute. [GLFW_MAXIMIZED](@ref window_attribs_wnd) window hint and attribute.
@subsection news_32_icon Window icon support
GLFW now supports setting the icon of windows with @ref glfwSetWindowIcon.
@subsection news_32_focus Window input focus control @subsection news_32_focus Window input focus control
GLFW now supports giving windows input focus with @ref glfwFocusWindow. GLFW now supports giving windows input focus with @ref glfwFocusWindow.

View File

@ -617,6 +617,26 @@ glfwSetWindowTitle(window, u8"This is always a UTF-8 string");
@endcode @endcode
@subsection window_icon Window icon
Decorated windows have icons on some platforms. You can set this icon by
specifying a list of candidate images with @ref glfwSetWindowIcon.
@code
GLFWimage images[2];
images[0] = load_icon("my_icon.png");
images[1] = load_icon("my_icon_small.png");
glfwSetWindowIcon(window, 2, images);
@endcode
To revert to the default window icon, pass in an empty image array.
@code
glfwSetWindowIcon(window, 0, NULL);
@endcode
@subsection window_monitor Window monitor @subsection window_monitor Window monitor
Full screen windows are associated with a specific monitor. You can get the Full screen windows are associated with a specific monitor. You can get the

View File

@ -1913,6 +1913,45 @@ GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* window, int value);
*/ */
GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title); GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title);
/*! @brief Sets the icon for the specified window.
*
* This function sets the icon of the specified window. If passed an array of
* candidate images, those of or closest to the sizes desired by the system are
* selected. If no images are specified, the window reverts to its default
* icon.
*
* The desired image sizes varies depending on platform and system settings.
* The selected images will be rescaled as needed. Good sizes include 16x16,
* 32x32 and 48x48.
*
* @param[in] window The window whose icon to set.
* @param[in] count The number of images in the specified array, or zero to
* revert to the default window icon.
* @param[in] images The images to create the icon from. This is ignored if
* count is zero.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
* @pointer_lifetime The specified image data is copied before this function
* returns.
*
* @remark @osx The GLFW window has no icon, as it is not a document
* window, but the dock icon will be the same as the application bundle's icon.
* For more information on bundles, see the
* [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/)
* in the Mac Developer Library.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref window_icon
*
* @since Added in version 3.2.
*
* @ingroup window
*/
GLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* images);
/*! @brief Retrieves the position of the client area of the specified window. /*! @brief Retrieves the position of the client area of the specified window.
* *
* This function retrieves the position, in screen coordinates, of the * This function retrieves the position, in screen coordinates, of the

View File

@ -1032,6 +1032,12 @@ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char *title)
[window->ns.object setTitle:[NSString stringWithUTF8String:title]]; [window->ns.object setTitle:[NSString stringWithUTF8String:title]];
} }
void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
int count, const GLFWimage* images)
{
// Regular windows do not have icons
}
void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
{ {
const NSRect contentRect = const NSRect contentRect =

View File

@ -617,6 +617,11 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window);
*/ */
void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title); void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title);
/*! @copydoc glfwSetWindowIcon
* @ingroup platform
*/
void _glfwPlatformSetWindowIcon(_GLFWwindow* window, int count, const GLFWimage* images);
/*! @copydoc glfwGetWindowPos /*! @copydoc glfwGetWindowPos
* @ingroup platform * @ingroup platform
*/ */

View File

@ -403,6 +403,13 @@ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
mir_surface_spec_release(spec); mir_surface_spec_release(spec);
} }
void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
int count, const GLFWimage* images)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Mir: Unsupported function %s", __PRETTY_FUNCTION__);
}
void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
{ {
MirSurfaceSpec* spec; MirSurfaceSpec* spec;

View File

@ -191,6 +191,8 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)(
typedef struct _GLFWwindowWin32 typedef struct _GLFWwindowWin32
{ {
HWND handle; HWND handle;
HICON bigIcon;
HICON smallIcon;
GLFWbool cursorTracked; GLFWbool cursorTracked;
GLFWbool iconified; GLFWbool iconified;

View File

@ -27,6 +27,7 @@
#include "internal.h" #include "internal.h"
#include <limits.h>
#include <stdlib.h> #include <stdlib.h>
#include <malloc.h> #include <malloc.h>
#include <string.h> #include <string.h>
@ -66,6 +67,112 @@ static DWORD getWindowExStyle(const _GLFWwindow* window)
return style; return style;
} }
// Returns the image whose area most closely matches the desired one
//
static const GLFWimage* chooseImage(int count, const GLFWimage* images,
int width, int height)
{
int i, leastDiff = INT_MAX;
const GLFWimage* closest = NULL;
for (i = 0; i < count; i++)
{
const int currDiff = abs(images[i].width * images[i].height -
width * height);
if (currDiff < leastDiff)
{
closest = images + i;
leastDiff = currDiff;
}
}
return closest;
}
// Creates an RGBA icon or cursor
//
static HICON createIcon(const GLFWimage* image,
int xhot, int yhot, GLFWbool icon)
{
int i;
HDC dc;
HICON handle;
HBITMAP color, mask;
BITMAPV5HEADER bi;
ICONINFO ii;
unsigned char* target = NULL;
unsigned char* source = image->pixels;
ZeroMemory(&bi, sizeof(bi));
bi.bV5Size = sizeof(BITMAPV5HEADER);
bi.bV5Width = image->width;
bi.bV5Height = -image->height;
bi.bV5Planes = 1;
bi.bV5BitCount = 32;
bi.bV5Compression = BI_BITFIELDS;
bi.bV5RedMask = 0x00ff0000;
bi.bV5GreenMask = 0x0000ff00;
bi.bV5BlueMask = 0x000000ff;
bi.bV5AlphaMask = 0xff000000;
dc = GetDC(NULL);
color = CreateDIBSection(dc,
(BITMAPINFO*) &bi,
DIB_RGB_COLORS,
(void**) &target,
NULL,
(DWORD) 0);
ReleaseDC(NULL, dc);
if (!color)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Win32: Failed to create RGBA bitmap");
return NULL;
}
mask = CreateBitmap(image->width, image->height, 1, 1, NULL);
if (!mask)
{
_glfwInputError(GLFW_PLATFORM_ERROR,
"Win32: Failed to create mask bitmap");
DeleteObject(color);
return NULL;
}
for (i = 0; i < image->width * image->height; i++)
{
target[0] = source[2];
target[1] = source[1];
target[2] = source[0];
target[3] = source[3];
target += 4;
source += 4;
}
ZeroMemory(&ii, sizeof(ii));
ii.fIcon = icon;
ii.xHotspot = xhot;
ii.yHotspot = yhot;
ii.hbmMask = mask;
ii.hbmColor = color;
handle = CreateIconIndirect(&ii);
DeleteObject(color);
DeleteObject(mask);
if (!handle)
{
if (icon)
_glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create icon");
else
_glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create cursor");
}
return handle;
}
// Translate client window size to full window size according to styles // Translate client window size to full window size according to styles
// //
static void getFullWindowSize(DWORD style, DWORD exStyle, static void getFullWindowSize(DWORD style, DWORD exStyle,
@ -886,6 +993,12 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
} }
destroyWindow(window); destroyWindow(window);
if (window->win32.bigIcon)
DestroyIcon(window->win32.bigIcon);
if (window->win32.smallIcon)
DestroyIcon(window->win32.smallIcon);
} }
void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
@ -902,6 +1015,37 @@ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
free(wideTitle); free(wideTitle);
} }
void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
int count, const GLFWimage* images)
{
HICON bigIcon = NULL, smallIcon = NULL;
if (count)
{
const GLFWimage* bigImage = chooseImage(count, images,
GetSystemMetrics(SM_CXICON),
GetSystemMetrics(SM_CYICON));
const GLFWimage* smallImage = chooseImage(count, images,
GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON));
bigIcon = createIcon(bigImage, 0, 0, GLFW_TRUE);
smallIcon = createIcon(smallImage, 0, 0, GLFW_TRUE);
}
SendMessage(window->win32.handle, WM_SETICON, ICON_BIG, (LPARAM) bigIcon);
SendMessage(window->win32.handle, WM_SETICON, ICON_SMALL, (LPARAM) smallIcon);
if (window->win32.bigIcon)
DestroyIcon(window->win32.bigIcon);
if (window->win32.smallIcon)
DestroyIcon(window->win32.smallIcon);
window->win32.bigIcon = bigIcon;
window->win32.smallIcon = smallIcon;
}
void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
{ {
POINT pos = { 0, 0 }; POINT pos = { 0, 0 };
@ -1236,61 +1380,7 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
const GLFWimage* image, const GLFWimage* image,
int xhot, int yhot) int xhot, int yhot)
{ {
HDC dc; cursor->win32.handle = (HCURSOR) createIcon(image, xhot, yhot, GLFW_FALSE);
HBITMAP bitmap, mask;
BITMAPV5HEADER bi;
ICONINFO ii;
DWORD* target = 0;
BYTE* source = (BYTE*) image->pixels;
int i;
ZeroMemory(&bi, sizeof(bi));
bi.bV5Size = sizeof(BITMAPV5HEADER);
bi.bV5Width = image->width;
bi.bV5Height = -image->height;
bi.bV5Planes = 1;
bi.bV5BitCount = 32;
bi.bV5Compression = BI_BITFIELDS;
bi.bV5RedMask = 0x00ff0000;
bi.bV5GreenMask = 0x0000ff00;
bi.bV5BlueMask = 0x000000ff;
bi.bV5AlphaMask = 0xff000000;
dc = GetDC(NULL);
bitmap = CreateDIBSection(dc, (BITMAPINFO*) &bi, DIB_RGB_COLORS,
(void**) &target, NULL, (DWORD) 0);
ReleaseDC(NULL, dc);
if (!bitmap)
return GLFW_FALSE;
mask = CreateBitmap(image->width, image->height, 1, 1, NULL);
if (!mask)
{
DeleteObject(bitmap);
return GLFW_FALSE;
}
for (i = 0; i < image->width * image->height; i++, target++, source += 4)
{
*target = (source[3] << 24) |
(source[0] << 16) |
(source[1] << 8) |
source[2];
}
ZeroMemory(&ii, sizeof(ii));
ii.fIcon = FALSE;
ii.xHotspot = xhot;
ii.yHotspot = yhot;
ii.hbmMask = mask;
ii.hbmColor = bitmap;
cursor->win32.handle = (HCURSOR) CreateIconIndirect(&ii);
DeleteObject(bitmap);
DeleteObject(mask);
if (!cursor->win32.handle) if (!cursor->win32.handle)
return GLFW_FALSE; return GLFW_FALSE;

View File

@ -447,6 +447,18 @@ GLFWAPI void glfwSetWindowTitle(GLFWwindow* handle, const char* title)
_glfwPlatformSetWindowTitle(window, title); _glfwPlatformSetWindowTitle(window, title);
} }
GLFWAPI void glfwSetWindowIcon(GLFWwindow* handle,
int count, const GLFWimage* images)
{
_GLFWwindow* window = (_GLFWwindow*) handle;
assert(window != NULL);
assert(count >= 0);
assert(count == 0 || images != NULL);
_GLFW_REQUIRE_INIT();
_glfwPlatformSetWindowIcon(window, count, images);
}
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;

View File

@ -383,6 +383,13 @@ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
wl_shell_surface_set_title(window->wl.shell_surface, title); wl_shell_surface_set_title(window->wl.shell_surface, title);
} }
void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
int count, const GLFWimage* images)
{
// TODO
fprintf(stderr, "_glfwPlatformSetWindowIcon not implemented yet\n");
}
void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) void _glfwPlatformGetWindowPos(_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

View File

@ -454,6 +454,8 @@ static void detectEWMH(void)
getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_NAME"); getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_NAME");
_glfw.x11.NET_WM_ICON_NAME = _glfw.x11.NET_WM_ICON_NAME =
getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_ICON_NAME"); getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_ICON_NAME");
_glfw.x11.NET_WM_ICON =
getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_ICON");
_glfw.x11.NET_WM_PID = _glfw.x11.NET_WM_PID =
getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_PID"); getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_PID");
_glfw.x11.NET_WM_PING = _glfw.x11.NET_WM_PING =

View File

@ -167,6 +167,7 @@ typedef struct _GLFWlibraryX11
Atom WM_DELETE_WINDOW; Atom WM_DELETE_WINDOW;
Atom NET_WM_NAME; Atom NET_WM_NAME;
Atom NET_WM_ICON_NAME; Atom NET_WM_ICON_NAME;
Atom NET_WM_ICON;
Atom NET_WM_PID; Atom NET_WM_PID;
Atom NET_WM_PING; Atom NET_WM_PING;
Atom NET_WM_STATE; Atom NET_WM_STATE;

View File

@ -1536,6 +1536,51 @@ void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
XFlush(_glfw.x11.display); XFlush(_glfw.x11.display);
} }
void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
int count, const GLFWimage* images)
{
if (count)
{
int i, j, longCount = 0;
for (i = 0; i < count; i++)
longCount += 2 + images[i].width * images[i].height;
long* icon = calloc(longCount, sizeof(long));
long* target = icon;
for (i = 0; i < count; i++)
{
*target++ = images[i].width;
*target++ = images[i].height;
for (j = 0; j < images[i].width * images[i].height; i++)
{
*target++ = (images[i].pixels[i * 4 + 0] << 16) |
(images[i].pixels[i * 4 + 1] << 8) |
(images[i].pixels[i * 4 + 2] << 0) |
(images[i].pixels[i * 4 + 3] << 24);
}
}
XChangeProperty(_glfw.x11.display, window->x11.handle,
_glfw.x11.NET_WM_ICON,
XA_CARDINAL, 32,
PropModeReplace,
(unsigned char*) icon,
longCount);
free(icon);
}
else
{
XDeleteProperty(_glfw.x11.display, window->x11.handle,
_glfw.x11.NET_WM_ICON);
}
XFlush(_glfw.x11.display);
}
void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
{ {
Window child; Window child;