summaryrefslogtreecommitdiff
path: root/kon/konwinlib.h
diff options
context:
space:
mode:
Diffstat (limited to 'kon/konwinlib.h')
-rw-r--r--kon/konwinlib.h308
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);