diff --git a/examples/basic/clipboard.c b/examples/basic/clipboard.c index c792db8..e6a7502 100644 --- a/examples/basic/clipboard.c +++ b/examples/basic/clipboard.c @@ -2,7 +2,7 @@ MwWidget window, instructions, text; -void resize(MwWidget handle, void* user_data, void* call_data) { +static void resize(MwWidget handle, void* user_data, void* call_data) { unsigned int w, h; (void)user_data; @@ -23,7 +23,7 @@ void resize(MwWidget handle, void* user_data, void* call_data) { MwNheight, h - 125 - 50 * 3, NULL); } -void clipboard(MwWidget handle, void* user_data, void* call_data) { +static void clipboard(MwWidget handle, void* user_data, void* call_data) { char* clipboard = call_data; (void)handle; @@ -36,6 +36,10 @@ void clipboard(MwWidget handle, void* user_data, void* call_data) { MwForceRender(window); } +static void tick(MwWidget handle, void* user_data, void* call_data) { + MwGetClipboard(handle); +} + int main() { MwLibraryInit(); @@ -53,6 +57,7 @@ int main() { MwAddUserHandler(window, MwNclipboardHandler, clipboard, NULL); MwAddUserHandler(instructions, MwNclipboardHandler, clipboard, NULL); MwAddUserHandler(text, MwNclipboardHandler, clipboard, NULL); + MwAddUserHandler(window, MwNtickHandler, tick, NULL); resize(window, NULL, NULL); diff --git a/examples/basic/example.c b/examples/basic/example.c index 89cc7d3..7e13acd 100644 --- a/examples/basic/example.c +++ b/examples/basic/example.c @@ -16,8 +16,8 @@ void handler_theme(MwWidget handle, void* user_data, void* call_data) { (void)call_data; MwVaApply(window, - MwNdarkTheme, MwGetInteger(window, MwNdarkTheme) == 0 ? 1 : 0, - NULL); + MwNdarkTheme, MwGetInteger(window, MwNdarkTheme) == 0 ? 1 : 0, + NULL); } void handler_look(MwWidget handle, void* user_data, void* call_data) { @@ -26,8 +26,8 @@ void handler_look(MwWidget handle, void* user_data, void* call_data) { (void)call_data; MwVaApply(window, - MwNmodernLook, MwGetInteger(window, MwNmodernLook) == 0 ? 1 : 0, - NULL); + MwNmodernLook, MwGetInteger(window, MwNmodernLook) == 0 ? 1 : 0, + NULL); } void handler_font(MwWidget handle, void* user_data, void* call_data) { @@ -36,8 +36,8 @@ void handler_font(MwWidget handle, void* user_data, void* call_data) { (void)call_data; MwVaApply(window, - MwNbitmapFont, MwGetInteger(window, MwNbitmapFont) == 0 ? 1 : 0, - NULL); + MwNbitmapFont, MwGetInteger(window, MwNbitmapFont) == 0 ? 1 : 0, + NULL); } void resize(MwWidget handle, void* user_data, void* call_data) { diff --git a/examples/vkdemos/vulkan.c b/examples/vkdemos/vulkan.c index 29547ce..1f260f6 100644 --- a/examples/vkdemos/vulkan.c +++ b/examples/vkdemos/vulkan.c @@ -292,8 +292,8 @@ void vulkan_setup(MwWidget handle) { swapchainCreateInfo.imageExtent = (VkExtent2D){.width = ow, .height = oh}, swapchainCreateInfo.imageArrayLayers = 1, swapchainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | - VK_IMAGE_USAGE_TRANSFER_DST_BIT | - VK_IMAGE_USAGE_SAMPLED_BIT; + VK_IMAGE_USAGE_TRANSFER_DST_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT; // th is how we specify no transformation. swapchainCreateInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; swapchainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; diff --git a/include/Mw/Core.h b/include/Mw/Core.h index 0c0e6b6..4d3e4b9 100644 --- a/include/Mw/Core.h +++ b/include/Mw/Core.h @@ -357,9 +357,16 @@ MWDECL void MwGetScreenSize(MwWidget handle, MwRect* rect); /*! * @brief Reports whether a widget reports global or local coordinates upon GetXY/SetXY. Anything with a parent reports local, and most backends report global coordinates being supported for top level windows, but some (Wayland) do not. + * @param handle Widget */ MWDECL int MwGetCoordinateType(MwWidget handle); +/*! + * @brief Queue to get clipboard content + * @param handle Widget + */ +MWDECL void MwGetClipboard(MwWidget handle); + #ifdef __cplusplus } #endif diff --git a/include/Mw/LowLevel/X11.h b/include/Mw/LowLevel/X11.h index ec36c04..529a570 100644 --- a/include/Mw/LowLevel/X11.h +++ b/include/Mw/LowLevel/X11.h @@ -30,9 +30,17 @@ struct _MwLLX11 { Colormap colormap; Atom wm_delete; Atom wm_protocols; + Atom utf8_string; + Atom compound_text; + Atom text; + Atom clipboard; + Atom selection; XIM xim; XIC xic; + long clipboard_time; + int clipboard_pending; /* 1 if UTF8_STRING, 2 if COMPOUNd_TEXT, 3 if TEXT, 4 if STRING, otherwise 0 */ + int top; int toplevel; int grabbed; diff --git a/pl/rules.pl b/pl/rules.pl index 5433a17..748ce8a 100644 --- a/pl/rules.pl +++ b/pl/rules.pl @@ -35,13 +35,13 @@ if (grep(/^wayland$/, @backends)) { } scan_wayland_protocol_core(); - scan_wayland_protocol("stable", "xdg-shell", ""); - scan_wayland_protocol("stable", "viewporter", ""); - scan_wayland_protocol("stable", "tablet", "-v2"); - scan_wayland_protocol("staging", "xdg-toplevel-icon", "-v1"); - scan_wayland_protocol("staging", "cursor-shape", "-v1"); - scan_wayland_protocol("unstable", "xdg-decoration", "-unstable-v1"); - scan_wayland_protocol("unstable", "primary-selection", "-unstable-v1"); + scan_wayland_protocol("stable", "xdg-shell", ""); + scan_wayland_protocol("stable", "viewporter", ""); + scan_wayland_protocol("stable", "tablet", "-v2"); + scan_wayland_protocol("staging", "xdg-toplevel-icon", "-v1"); + scan_wayland_protocol("staging", "cursor-shape", "-v1"); + scan_wayland_protocol("unstable", "xdg-decoration", "-unstable-v1"); + scan_wayland_protocol("unstable", "primary-selection", "-unstable-v1"); scan_wayland_protocol("unstable", "pointer-constraints", "-unstable-v1"); $gl_libs = "-lGL -lGLU"; diff --git a/pl/utils.pl b/pl/utils.pl index 77131a0..ce738fc 100644 --- a/pl/utils.pl +++ b/pl/utils.pl @@ -58,7 +58,6 @@ sub use_backend { } } - sub scan_wayland_protocol_core { my $proto_c = "src/backend/wayland-core-protocol.c"; diff --git a/src/backend/x11.c b/src/backend/x11.c index de69fff..de68867 100644 --- a/src/backend/x11.c +++ b/src/backend/x11.c @@ -2,6 +2,8 @@ #include "../../external/stb_ds.h" +#define ClipboardTimeout 100 + typedef struct mwm_hints { unsigned long flags; unsigned long functions; @@ -215,6 +217,14 @@ static MwLL MwLLCreateImpl(MwLL parent, int x, int y, int width, int height) { r->x11.wm_protocols = XInternAtom(r->x11.display, "WM_PROTOCOLS", False); XSetWMProtocols(r->x11.display, r->x11.window, &r->x11.wm_delete, 1); + r->x11.utf8_string = XInternAtom(r->x11.display, "UTF8_STRING", False); + r->x11.compound_text = XInternAtom(r->x11.display, "COMPOUND_TEXT", False); + r->x11.text = XInternAtom(r->x11.display, "TEXT", False); + r->x11.clipboard = XInternAtom(r->x11.display, "CLIPBOARD", False); + r->x11.selection = XInternAtom(r->x11.display, "_MILSKO_SELECTION_", False); + + r->x11.clipboard_pending = 0; + r->x11.gc = XCreateGC(r->x11.display, r->x11.window, 0, NULL); create_pixmap(r); @@ -414,7 +424,11 @@ static void MwLLFreeColorImpl(MwLLColor color) { static int MwLLPendingImpl(MwLL handle) { XEvent ev; - if(XCheckTypedWindowEvent(handle->x11.display, handle->x11.window, ClientMessage, &ev) || XCheckWindowEvent(handle->x11.display, handle->x11.window, mask, &ev)) { + if(handle->x11.clipboard_pending && (MwTimeGetTick() - handle->x11.clipboard_time) >= ClipboardTimeout) { + return 1; + } + + if(XCheckTypedWindowEvent(handle->x11.display, handle->x11.window, ClientMessage, &ev) || XCheckTypedWindowEvent(handle->x11.display, handle->x11.window, SelectionNotify, &ev) || XCheckWindowEvent(handle->x11.display, handle->x11.window, mask, &ev)) { XPutBackEvent(handle->x11.display, &ev); return 1; } @@ -424,7 +438,33 @@ static int MwLLPendingImpl(MwLL handle) { static void MwLLNextEventImpl(MwLL handle) { XEvent ev; - while(XCheckTypedWindowEvent(handle->x11.display, handle->x11.window, ClientMessage, &ev) || XCheckWindowEvent(handle->x11.display, handle->x11.window, mask, &ev)) { + if(handle->x11.clipboard_pending && (MwTimeGetTick() - handle->x11.clipboard_time) >= ClipboardTimeout) { + Atom a = handle->x11.selection; + + handle->x11.clipboard_pending++; + handle->x11.clipboard_time = MwTimeGetTick(); + + switch(handle->x11.clipboard_pending) { + case 2: + a = handle->x11.compound_text; + break; + case 3: + a = handle->x11.text; + break; + case 4: + a = XA_STRING; + break; + case 5: + handle->x11.clipboard_pending = 0; + break; + } + + if(a != handle->x11.selection) { + XConvertSelection(handle->x11.display, handle->x11.clipboard, a, handle->x11.selection, handle->x11.window, CurrentTime); + } + } + + while(XCheckTypedWindowEvent(handle->x11.display, handle->x11.window, ClientMessage, &ev) || XCheckTypedWindowEvent(handle->x11.display, handle->x11.window, SelectionNotify, &ev) || XCheckWindowEvent(handle->x11.display, handle->x11.window, mask, &ev)) { int render = 0; if(ev.type == Expose) { handle->x11.force_render = 0; @@ -563,6 +603,49 @@ static void MwLLNextEventImpl(MwLL handle) { MwLLDispatch(handle, key_released, &n); } } + } else if(ev.type == SelectionNotify) { + handle->x11.clipboard_pending = 0; + + if(ev.xselection.property != None) { + Atom type; + int format; + unsigned long nitems, after, size; + unsigned char* pdat; + int ret = XGetWindowProperty(handle->x11.display, handle->x11.window, handle->x11.selection, 0, 0, False, AnyPropertyType, &type, &format, &nitems, &after, &pdat); + + if(pdat != NULL) XFree(pdat); + + size = after; + + if(ret == Success && type != None && format == 8) { + char* buf = malloc(size + 1); + int offset; + + for(offset = 0; after > 0; offset += nitems) { + ret = XGetWindowProperty(handle->x11.display, handle->x11.window, handle->x11.selection, offset / 4, after / 4 + 1, False, AnyPropertyType, &type, &format, &nitems, &after, &pdat); + + if(ret != Success) { + free(buf); + buf = NULL; + break; + } + + memcpy(buf + offset, pdat, nitems); + + XFree(pdat); + } + + if(buf != NULL) { + buf[size] = 0; + + MwLLDispatch(handle, clipboard, buf); + + free(buf); + } + } + + XDeleteProperty(handle->x11.display, handle->x11.window, handle->x11.selection); + } } if(render) { int x, y; @@ -961,9 +1044,12 @@ static void MwLLSetClipboardImpl(MwLL handle, const char* text) { } static void MwLLGetClipboardImpl(MwLL handle) { - /* TODO */ + if(handle->x11.clipboard_pending) return; - (void)handle; + XConvertSelection(handle->x11.display, handle->x11.clipboard, handle->x11.utf8_string, handle->x11.selection, handle->x11.window, CurrentTime); + + handle->x11.clipboard_pending = 1; + handle->x11.clipboard_time = MwTimeGetTick(); } static void MwLLMakeToolWindowImpl(MwLL handle) { diff --git a/src/core.c b/src/core.c index 852aa82..4031267 100644 --- a/src/core.c +++ b/src/core.c @@ -134,8 +134,8 @@ static void lldarkthemehandler(MwLL handle, void* data) { if(IsFirstVisible(h)) { MwVaApply(h, - MwNdarkTheme, *ptr, - NULL); + MwNdarkTheme, *ptr, + NULL); MwDispatchUserHandler(h, MwNdarkThemeHandler, data); } @@ -449,7 +449,7 @@ void MwSetInteger(MwWidget handle, const char* key, int n) { if(strcmp(key, MwNforceInverted) == 0) { MwForceRender(handle); } - if(strcmp(key, MwNmodernLook) == 0 || strcmp(key, MwNdarkTheme) == 0 || strcmp(key, MwNbitmapFont) == 0){ + if(strcmp(key, MwNmodernLook) == 0 || strcmp(key, MwNdarkTheme) == 0 || strcmp(key, MwNbitmapFont) == 0) { force_render_all(handle); } } @@ -762,8 +762,8 @@ void MwGrabPointer(MwWidget handle, int toggle) { void MwSetDarkTheme(MwWidget handle, int toggle) { MwVaApply(handle, - MwNdarkTheme, toggle, - NULL); + MwNdarkTheme, toggle, + NULL); } MwWidget MwGetParent(MwWidget handle) { @@ -856,4 +856,8 @@ int MwGetCoordinateType(MwWidget handle) { } else { return MwCoordinatesLocal; } -}; +} + +void MwGetClipboard(MwWidget handle) { + MwLLGetClipboard(handle->lowlevel); +} diff --git a/src/draw.c b/src/draw.c index dfc6981..12a0e70 100644 --- a/src/draw.c +++ b/src/draw.c @@ -283,7 +283,6 @@ static void MwDrawFrameEx_simple(MwWidget handle, MwRect* rect, MwLLColor color, int ColorDiff = get_color_diff(handle); MwLLColor darker = MwLightenColor(handle, color, -ColorDiff * 3 / 2 + diff, -ColorDiff * 3 / 2 + diff, -ColorDiff * 3 / 2 + diff); MwLLColor lighter = same ? MwLightenColor(handle, darker, 0, 0, 0) : MwLightenColor(handle, color, ColorDiff - diff, ColorDiff - diff, ColorDiff - diff); - int i; int roundness = MwGetInteger(handle, MwNroundness); if(roundness == MwDEFAULT) roundness = DEFAULT_ROUNDNESS; @@ -345,9 +344,11 @@ static void MwDrawFrameEx_simple(MwWidget handle, MwRect* rect, MwLLColor color, static void frame_border_complex(MwWidget handle, MwRect* rect, MwLLColor lighter, MwLLColor darker, int invert, int border, int diff, int same, int rounded) { MwPoint p[7]; int roundness = MwGetInteger(handle, MwNroundness); - MwRect r = *rect; int i; + (void)diff; + (void)same; + if(roundness == MwDEFAULT) roundness = DEFAULT_ROUNDNESS; if(roundness >= MAX_ROUNDNESS) roundness = MAX_ROUNDNESS; if(rounded && rect->height >= (roundness * 2) && rect->width >= (roundness * 2) && roundness > 0) { @@ -493,8 +494,7 @@ static void MwDrawFrameEx_complex(MwWidget handle, MwRect* rect, MwLLColor color int ColorDiff = get_color_diff(handle); MwLLColor darker = MwLightenColor(handle, color, -ColorDiff * 3 / 2 + diff, -ColorDiff * 3 / 2 + diff, -ColorDiff * 3 / 2 + diff); MwLLColor lighter = same ? MwLightenColor(handle, darker, 0, 0, 0) : MwLightenColor(handle, color, (ColorDiff / 2) - diff, (ColorDiff / 2) - diff, (ColorDiff / 2) - diff); - int i; - MwRect r = *rect; + MwRect r = *rect; frame_border_complex(handle, rect, lighter, darker, invert, border, diff, same, rounded); diff --git a/src/lowlevel.c b/src/lowlevel.c index 66c3d1d..597a23a 100644 --- a/src/lowlevel.c +++ b/src/lowlevel.c @@ -54,7 +54,7 @@ void (*MwLLGetScreenSize)(MwLL handle, MwRect* rect) = NULL; void MwLLCreateCommon(MwLL handle) { handle->common.handler = malloc(sizeof(*handle->common.handler)); memset(handle->common.handler, 0, sizeof(*handle->common.handler)); - + handle->common.supports_transparency = 0; } diff --git a/src/widget/vulkan.c b/src/widget/vulkan.c index a67c474..f6ef863 100644 --- a/src/widget/vulkan.c +++ b/src/widget/vulkan.c @@ -229,7 +229,7 @@ static MwErrorEnum vulkan_instance_setup(MwWidget handle, vulkan_t* o) { if(handle->lowlevel->common.type == MwLLBackendWayland) { arrput(enabledExtensions, VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); /* take this opprutunity to set the widget to always render */ - MwWaylandAlwaysRender = MwTRUE; + MwWaylandAlwaysRender = MwTRUE; } #endif