diff options
| author | KonAki <konaki@anokonaki.org> | 2026-10-09 02:50:22 +0800 |
|---|---|---|
| committer | KonAki <konaki@anokonaki.org> | 2026-10-09 02:50:22 +0800 |
| commit | 72ab7350ba8d5a1471f2bbbadd7f225065dd125d (patch) | |
| tree | 7c48c117067bda1eb41d741cd3249286250c036a /kon/konwinlib.h | |
| parent | 12c955cf1911edc3fbab9d675c14204058b4a134 (diff) | |
Add window tricks: opacity, topmost, click through, message box
Diffstat (limited to 'kon/konwinlib.h')
| -rw-r--r-- | kon/konwinlib.h | 308 |
1 files changed, 307 insertions, 1 deletions
diff --git a/kon/konwinlib.h b/kon/konwinlib.h index a718147..17b7068 100644 --- a/kon/konwinlib.h +++ b/kon/konwinlib.h @@ -23,6 +23,10 @@ * SOFTWARE. */ +#if !defined(_WIN32) && !defined(_POSIX_C_SOURCE) +#define _POSIX_C_SOURCE 200809L +#endif + #ifndef KONWINLIB_H #define KONWINLIB_H @@ -122,6 +126,32 @@ void kon_setWindowSize(kon_window_t *window, int width, int height); void kon_getWindowPos(kon_window_t *window, int *x, int *y); void kon_getWindowSize(kon_window_t *window, int *width, int *height); +/*** window tricks ***/ + +typedef enum kon_messageType { + KON_MESSAGE_INFO = 0, + KON_MESSAGE_WARNING, + KON_MESSAGE_ERROR +} kon_messageType_t; + +/* the whole screen, and the cursor on it, not in your window */ +void kon_getScreenSize(int *width, int *height); +void kon_getCursorPos(int *x, int *y); +void kon_setCursorPos(int x, int y); + +void kon_setWindowTitle(kon_window_t *window, const char *title); +void kon_setWindowDecorated(kon_window_t *window, bool decorated); +void kon_setWindowTopmost(kon_window_t *window, bool topmost); + +/* 0 to 1. on X11 this needs a compositor */ +void kon_setWindowOpacity(kon_window_t *window, float opacity); + +/* mouse clicks go to whatever is behind the window */ +void kon_setWindowClickThrough(kon_window_t *window, bool enabled); + +/* a real os dialog, waits until it is closed. works without a window. X11 needs zenity, kdialog or xmessage, otherwise it prints to stderr */ +void kon_messageBox(const char *title, const char *message, kon_messageType_t type); + int kon_pollEvent(kon_window_t *window, kon_event_t *event); void kon_blitPixels(kon_window_t *window, const uint32_t *pixels, int width, int height); @@ -431,6 +461,53 @@ void kon_getWindowSize(kon_window_t *window, int *width, int *height) { *height = (int)h; } +EM_JS(void, kon_jsTitle_, (const char *title), { document.title = UTF8ToString(title); }); +EM_JS(void, kon_jsAlert_, (const char *title, const char *message), { alert(UTF8ToString(title) + "\n\n" + UTF8ToString(message)); }); +EM_JS(int, kon_jsScreenWidth_, (void), { return screen.width; }); +EM_JS(int, kon_jsScreenHeight_, (void), { return screen.height; }); +EM_JS(void, kon_jsOpacity_, (const char *id, float value), { var c = document.querySelector(UTF8ToString(id)); if (c) c.style.opacity = value; }); +EM_JS(void, kon_jsClickThrough_, (const char *id, int on), { var c = document.querySelector(UTF8ToString(id)); if (c) c.style.pointerEvents = on ? "none" : "auto"; }); + +void kon_getScreenSize(int *width, int *height) { + if (width) *width = kon_jsScreenWidth_(); + if (height) *height = kon_jsScreenHeight_(); +} + +void kon_getCursorPos(int *x, int *y) { + if (x) *x = 0; + if (y) *y = 0; +} + +void kon_setCursorPos(int x, int y) { + (void)x; (void)y; +} + +void kon_setWindowTitle(kon_window_t *window, const char *title) { + (void)window; + if (title) kon_jsTitle_(title); +} + +void kon_setWindowDecorated(kon_window_t *window, bool decorated) { + (void)window; (void)decorated; +} + +void kon_setWindowTopmost(kon_window_t *window, bool topmost) { + (void)window; (void)topmost; +} + +void kon_setWindowOpacity(kon_window_t *window, float opacity) { + if (window) kon_jsOpacity_(window->canvasID, opacity < 0.0f ? 0.0f : (opacity > 1.0f ? 1.0f : opacity)); +} + +void kon_setWindowClickThrough(kon_window_t *window, bool enabled) { + if (window) kon_jsClickThrough_(window->canvasID, enabled ? 1 : 0); +} + +void kon_messageBox(const char *title, const char *message, kon_messageType_t type) { + (void)type; + kon_jsAlert_(title ? title : "", message ? message : ""); +} + int kon_pollEvent(kon_window_t *window, kon_event_t *event) { if (!window || !event) return 0; @@ -477,6 +554,9 @@ void kon_blitPixels(kon_window_t *window, const uint32_t *pixels, int width, int #include <X11/Xutil.h> #include <X11/Xatom.h> #include <X11/extensions/Xrandr.h> +#include <X11/extensions/shape.h> +#include <unistd.h> +#include <sys/wait.h> #include <X11/keysym.h> #include <string.h> @@ -891,6 +971,139 @@ static void kon_pumpX11_(kon_window_t *window) { } } +void kon_getScreenSize(int *width, int *height) { + if (!kon_ctx) return; + + int screen = DefaultScreen(kon_ctx->display); + if (width) *width = DisplayWidth(kon_ctx->display, screen); + if (height) *height = DisplayHeight(kon_ctx->display, screen); +} + +void kon_getCursorPos(int *x, int *y) { + if (!kon_ctx) return; + + Window root = RootWindow(kon_ctx->display, DefaultScreen(kon_ctx->display)); + Window rootRet, childRet; + int rootX = 0, rootY = 0, winX, winY; + unsigned int mask; + + XQueryPointer(kon_ctx->display, root, &rootRet, &childRet, &rootX, &rootY, &winX, &winY, &mask); + if (x) *x = rootX; + if (y) *y = rootY; +} + +void kon_setCursorPos(int x, int y) { + if (!kon_ctx) return; + + Window root = RootWindow(kon_ctx->display, DefaultScreen(kon_ctx->display)); + XWarpPointer(kon_ctx->display, None, root, 0, 0, 0, 0, x, y); + XFlush(kon_ctx->display); +} + +void kon_setWindowTitle(kon_window_t *window, const char *title) { + if (!window || !kon_ctx || !title) return; + + Atom netName = XInternAtom(kon_ctx->display, "_NET_WM_NAME", False); + Atom utf8 = XInternAtom(kon_ctx->display, "UTF8_STRING", False); + + XStoreName(kon_ctx->display, window->window, title); + XChangeProperty(kon_ctx->display, window->window, netName, utf8, 8, PropModeReplace, (const unsigned char *)title, (int)strlen(title)); + XFlush(kon_ctx->display); +} + +void kon_setWindowDecorated(kon_window_t *window, bool decorated) { + if (!window || !kon_ctx) return; + + Atom motif = XInternAtom(kon_ctx->display, "_MOTIF_WM_HINTS", False); + struct { unsigned long flags, functions, decorations; long input_mode; unsigned long status; } hints; + + memset(&hints, 0, sizeof(hints)); + hints.flags = 2; + hints.decorations = decorated ? 1 : 0; + XChangeProperty(kon_ctx->display, window->window, motif, motif, 32, PropModeReplace, (unsigned char *)&hints, 5); + XFlush(kon_ctx->display); +} + +void kon_setWindowTopmost(kon_window_t *window, bool topmost) { + if (!window || !kon_ctx) return; + + XEvent ev; + memset(&ev, 0, sizeof(ev)); + ev.xclient.type = ClientMessage; + ev.xclient.window = window->window; + ev.xclient.message_type = XInternAtom(kon_ctx->display, "_NET_WM_STATE", False); + ev.xclient.format = 32; + ev.xclient.data.l[0] = topmost ? 1 : 0; + ev.xclient.data.l[1] = (long)XInternAtom(kon_ctx->display, "_NET_WM_STATE_ABOVE", False); + ev.xclient.data.l[3] = 1; + + XSendEvent(kon_ctx->display, RootWindow(kon_ctx->display, DefaultScreen(kon_ctx->display)), False, + SubstructureRedirectMask | SubstructureNotifyMask, &ev); + XFlush(kon_ctx->display); +} + +void kon_setWindowOpacity(kon_window_t *window, float opacity) { + if (!window || !kon_ctx) return; + + Atom prop = XInternAtom(kon_ctx->display, "_NET_WM_WINDOW_OPACITY", False); + + if (opacity >= 1.0f) { + XDeleteProperty(kon_ctx->display, window->window, prop); + } else { + if (opacity < 0.0f) opacity = 0.0f; + unsigned long value = (unsigned long)((double)opacity * 4294967295.0); + XChangeProperty(kon_ctx->display, window->window, prop, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&value, 1); + } + XFlush(kon_ctx->display); +} + +void kon_setWindowClickThrough(kon_window_t *window, bool enabled) { + if (!window || !kon_ctx) return; + + int eventBase, errorBase; + if (!XShapeQueryExtension(kon_ctx->display, &eventBase, &errorBase)) return; + + if (enabled) { + XShapeCombineRectangles(kon_ctx->display, window->window, ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted); + } else { + XShapeCombineMask(kon_ctx->display, window->window, ShapeInput, 0, 0, None, ShapeSet); + } + XFlush(kon_ctx->display); +} + +/* returns 0 when the program does not exist */ +static int kon_runTool_(char *const argv[]) { + pid_t pid = fork(); + + if (pid < 0) return 0; + if (pid == 0) { + execvp(argv[0], argv); + _exit(127); + } + + int status = 0; + if (waitpid(pid, &status, 0) < 0) return 0; + return WIFEXITED(status) && WEXITSTATUS(status) != 127; +} + +void kon_messageBox(const char *title, const char *message, kon_messageType_t type) { + if (!title) title = ""; + if (!message) message = ""; + + const char *zenityKind = type == KON_MESSAGE_ERROR ? "--error" : (type == KON_MESSAGE_WARNING ? "--warning" : "--info"); + const char *kdialogKind = type == KON_MESSAGE_ERROR ? "--error" : (type == KON_MESSAGE_WARNING ? "--sorry" : "--msgbox"); + + char *zenity[] = { "zenity", (char *)zenityKind, "--title", (char *)title, "--text", (char *)message, NULL }; + char *kdialog[] = { "kdialog", (char *)kdialogKind, (char *)message, "--title", (char *)title, NULL }; + char *xmessage[] = { "xmessage", "-center", "-title", (char *)title, (char *)message, NULL }; + + if (kon_runTool_(zenity)) return; + if (kon_runTool_(kdialog)) return; + if (kon_runTool_(xmessage)) return; + + fprintf(stderr, "%s: %s\n", title, message); +} + int kon_pollEvent(kon_window_t *window, kon_event_t *event) { if (!window || !kon_ctx || !event) return 0; @@ -943,6 +1156,8 @@ struct kon_window { HWND hwnd; bool shouldClose; bool isTransparent; + bool resizable; + unsigned char alpha; int exitKey; unsigned short highSurrogate; kon_mainLoopFn liveFn; @@ -1198,6 +1413,8 @@ kon_window_t *kon_createWindow(const char *title, int x, int y, int width, int h if (!window) return NULL; window->isTransparent = (flags & KON_WINDOW_TRANSPARENT); + window->resizable = (flags & KON_WINDOW_RESIZABLE) != 0; + window->alpha = 255; window->exitKey = 0; DWORD style; @@ -1304,6 +1521,95 @@ void kon_getWindowSize(kon_window_t *window, int *width, int *height) { *height = rect.bottom - rect.top; } +void kon_getScreenSize(int *width, int *height) { + if (width) *width = GetSystemMetrics(SM_CXSCREEN); + if (height) *height = GetSystemMetrics(SM_CYSCREEN); +} + +void kon_getCursorPos(int *x, int *y) { + POINT p = { 0, 0 }; + + GetCursorPos(&p); + if (x) *x = p.x; + if (y) *y = p.y; +} + +void kon_setCursorPos(int x, int y) { + SetCursorPos(x, y); +} + +void kon_setWindowTitle(kon_window_t *window, const char *title) { + if (!window || !title) return; + SetWindowText(window->hwnd, title); +} + +void kon_setWindowDecorated(kon_window_t *window, bool decorated) { + if (!window) return; + + DWORD style = (DWORD)GetWindowLongPtr(window->hwnd, GWL_STYLE); + DWORD exStyle = (DWORD)GetWindowLongPtr(window->hwnd, GWL_EXSTYLE); + RECT client; + POINT topLeft = { 0, 0 }; + + GetClientRect(window->hwnd, &client); + ClientToScreen(window->hwnd, &topLeft); + + style &= ~(DWORD)(WS_OVERLAPPEDWINDOW | WS_POPUP); + if (decorated) { + style |= WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; + if (window->resizable) style |= WS_THICKFRAME | WS_MAXIMIZEBOX; + } else { + style |= WS_POPUP; + } + SetWindowLongPtr(window->hwnd, GWL_STYLE, (LONG_PTR)style); + + RECT outer = { 0, 0, client.right, client.bottom }; + AdjustWindowRectEx(&outer, style, FALSE, exStyle); + SetWindowPos(window->hwnd, NULL, topLeft.x + outer.left, topLeft.y + outer.top, + outer.right - outer.left, outer.bottom - outer.top, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); +} + +void kon_setWindowTopmost(kon_window_t *window, bool topmost) { + if (!window) return; + SetWindowPos(window->hwnd, topmost ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); +} + +static void kon_applyAlpha_(kon_window_t *window) { + if (window->isTransparent) return; + + DWORD exStyle = (DWORD)GetWindowLongPtr(window->hwnd, GWL_EXSTYLE); + SetWindowLongPtr(window->hwnd, GWL_EXSTYLE, (LONG_PTR)(exStyle | WS_EX_LAYERED)); + SetLayeredWindowAttributes(window->hwnd, 0, window->alpha, LWA_ALPHA); +} + +void kon_setWindowOpacity(kon_window_t *window, float opacity) { + if (!window) return; + + if (opacity < 0.0f) opacity = 0.0f; + if (opacity > 1.0f) opacity = 1.0f; + window->alpha = (unsigned char)(opacity * 255.0f + 0.5f); + kon_applyAlpha_(window); +} + +void kon_setWindowClickThrough(kon_window_t *window, bool enabled) { + if (!window) return; + + DWORD exStyle = (DWORD)GetWindowLongPtr(window->hwnd, GWL_EXSTYLE); + + if (enabled) exStyle |= WS_EX_TRANSPARENT | WS_EX_LAYERED; + else exStyle &= ~(DWORD)WS_EX_TRANSPARENT; + SetWindowLongPtr(window->hwnd, GWL_EXSTYLE, (LONG_PTR)exStyle); + + if (enabled) kon_applyAlpha_(window); +} + +void kon_messageBox(const char *title, const char *message, kon_messageType_t type) { + UINT icon = type == KON_MESSAGE_ERROR ? MB_ICONERROR : (type == KON_MESSAGE_WARNING ? MB_ICONWARNING : MB_ICONINFORMATION); + + MessageBox(NULL, message ? message : "", title ? title : "", MB_OK | MB_SETFOREGROUND | icon); +} + int kon_pollEvent(kon_window_t *window, kon_event_t *event) { if (!window || !event) return 0; @@ -1354,7 +1660,7 @@ void kon_blitPixels(kon_window_t *window, const uint32_t *pixels, int width, int POINT dstPos = { winRect.left, winRect.top }; POINT srcPos = { 0, 0 }; SIZE size = { width, height }; - BLENDFUNCTION blend = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; + BLENDFUNCTION blend = { AC_SRC_OVER, 0, window->alpha, AC_SRC_ALPHA }; UpdateLayeredWindow(window->hwnd, screenDC, &dstPos, &size, memDC, &srcPos, 0, &blend, ULW_ALPHA); |
