1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286 |
- //========================================================================
- // GLFW 3.4 X11 - www.glfw.org
- //------------------------------------------------------------------------
- // Copyright (c) 2002-2006 Marcus Geelnard
- // Copyright (c) 2006-2019 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 "backend_utils.h"
- #include "linux_notify.h"
- #include "../kitty/monotonic.h"
- #include <X11/cursorfont.h>
- #include <X11/Xmd.h>
- #include <string.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <limits.h>
- #include <errno.h>
- // Action for EWMH client messages
- #define _NET_WM_STATE_REMOVE 0
- #define _NET_WM_STATE_ADD 1
- #define _NET_WM_STATE_TOGGLE 2
- // Additional mouse button names for XButtonEvent
- #define Button6 6
- #define Button7 7
- // Motif WM hints flags
- #define MWM_HINTS_DECORATIONS 2
- #define MWM_DECOR_ALL 1
- #define _GLFW_XDND_VERSION 5
- // Wait for data to arrive using poll
- // This avoids blocking other threads via the per-display Xlib lock that also
- // covers GLX functions
- //
- static unsigned _glfwDispatchX11Events(void);
- static void
- handleEvents(monotonic_t timeout) {
- EVDBG("starting handleEvents(%.2f)", monotonic_t_to_s_double(timeout));
- int display_read_ok = pollForEvents(&_glfw.x11.eventLoopData, timeout, NULL);
- EVDBG("display_read_ok: %d", display_read_ok);
- if (display_read_ok) {
- unsigned dispatched = _glfwDispatchX11Events();
- (void)dispatched;
- EVDBG("dispatched %u X11 events", dispatched);
- }
- glfw_ibus_dispatch(&_glfw.x11.xkb.ibus);
- glfw_dbus_session_bus_dispatch();
- EVDBG("other dispatch done");
- if (_glfw.x11.eventLoopData.wakeup_fd_ready) check_for_wakeup_events(&_glfw.x11.eventLoopData);
- }
- static bool
- waitForX11Event(monotonic_t timeout) {
- // returns true if there is X11 data waiting to be read, does not run watches and timers
- monotonic_t end_time = glfwGetTime() + timeout;
- while(true) {
- if (timeout >= 0) {
- const int result = pollWithTimeout(_glfw.x11.eventLoopData.fds, 1, timeout);
- if (result > 0) return true;
- timeout = end_time - glfwGetTime();
- if (timeout <= 0) return false;
- if (result < 0 && (errno == EINTR || errno == EAGAIN)) continue;
- return false;
- } else {
- const int result = poll(_glfw.x11.eventLoopData.fds, 1, -1);
- if (result > 0) return true;
- if (result < 0 && (errno == EINTR || errno == EAGAIN)) continue;
- return false;
- }
- }
- }
- // Waits until a VisibilityNotify event arrives for the specified window or the
- // timeout period elapses (ICCCM section 4.2.2)
- //
- static bool waitForVisibilityNotify(_GLFWwindow* window)
- {
- XEvent dummy;
- while (!XCheckTypedWindowEvent(_glfw.x11.display,
- window->x11.handle,
- VisibilityNotify,
- &dummy))
- {
- if (!waitForX11Event(ms_to_monotonic_t(100ll)))
- return false;
- }
- return true;
- }
- // Returns whether the window is iconified
- //
- static int getWindowState(_GLFWwindow* window)
- {
- int result = WithdrawnState;
- struct {
- CARD32 state;
- Window icon;
- } *state = NULL;
- if (_glfwGetWindowPropertyX11(window->x11.handle,
- _glfw.x11.WM_STATE,
- _glfw.x11.WM_STATE,
- (unsigned char**) &state) >= 2)
- {
- result = state->state;
- }
- if (state)
- XFree(state);
- return result;
- }
- // Returns whether the event is a selection event
- //
- static Bool isSelectionEvent(Display* display UNUSED, XEvent* event, XPointer pointer UNUSED)
- {
- if (event->xany.window != _glfw.x11.helperWindowHandle)
- return False;
- return event->type == SelectionRequest ||
- event->type == SelectionNotify ||
- event->type == SelectionClear;
- }
- // Returns whether it is a _NET_FRAME_EXTENTS event for the specified window
- //
- static Bool isFrameExtentsEvent(Display* display UNUSED, XEvent* event, XPointer pointer)
- {
- _GLFWwindow* window = (_GLFWwindow*) pointer;
- return event->type == PropertyNotify &&
- event->xproperty.state == PropertyNewValue &&
- event->xproperty.window == window->x11.handle &&
- event->xproperty.atom == _glfw.x11.NET_FRAME_EXTENTS;
- }
- // Returns whether it is a property event for the specified selection transfer
- //
- static Bool isSelPropNewValueNotify(Display* display UNUSED, XEvent* event, XPointer pointer)
- {
- XEvent* notification = (XEvent*) pointer;
- return event->type == PropertyNotify &&
- event->xproperty.state == PropertyNewValue &&
- event->xproperty.window == notification->xselection.requestor &&
- event->xproperty.atom == notification->xselection.property;
- }
- // Translates an X event modifier state mask
- //
- static int translateState(int state)
- {
- int mods = 0;
- /* Need some way to expose hyper and meta without xkbcommon-x11 */
- if (state & ShiftMask)
- mods |= GLFW_MOD_SHIFT;
- if (state & ControlMask)
- mods |= GLFW_MOD_CONTROL;
- if (state & Mod1Mask)
- mods |= GLFW_MOD_ALT;
- if (state & Mod4Mask)
- mods |= GLFW_MOD_SUPER;
- if (state & LockMask)
- mods |= GLFW_MOD_CAPS_LOCK;
- if (state & Mod2Mask)
- mods |= GLFW_MOD_NUM_LOCK;
- return mods;
- }
- // Sends an EWMH or ICCCM event to the window manager
- //
- static void sendEventToWM(_GLFWwindow* window, Atom type,
- long a, long b, long c, long d, long e)
- {
- XEvent event = { ClientMessage };
- event.xclient.window = window->x11.handle;
- event.xclient.format = 32; // Data is 32-bit longs
- event.xclient.message_type = type;
- event.xclient.data.l[0] = a;
- event.xclient.data.l[1] = b;
- event.xclient.data.l[2] = c;
- event.xclient.data.l[3] = d;
- event.xclient.data.l[4] = e;
- XSendEvent(_glfw.x11.display, _glfw.x11.root,
- False,
- SubstructureNotifyMask | SubstructureRedirectMask,
- &event);
- }
- // Updates the normal hints according to the window settings
- //
- static void
- updateNormalHints(_GLFWwindow* window, int width, int height)
- {
- XSizeHints* hints = XAllocSizeHints();
- if (!window->monitor)
- {
- if (window->resizable)
- {
- if (window->minwidth != GLFW_DONT_CARE &&
- window->minheight != GLFW_DONT_CARE)
- {
- hints->flags |= PMinSize;
- hints->min_width = window->minwidth;
- hints->min_height = window->minheight;
- }
- if (window->maxwidth != GLFW_DONT_CARE &&
- window->maxheight != GLFW_DONT_CARE)
- {
- hints->flags |= PMaxSize;
- hints->max_width = window->maxwidth;
- hints->max_height = window->maxheight;
- }
- if (window->numer != GLFW_DONT_CARE &&
- window->denom != GLFW_DONT_CARE)
- {
- hints->flags |= PAspect;
- hints->min_aspect.x = hints->max_aspect.x = window->numer;
- hints->min_aspect.y = hints->max_aspect.y = window->denom;
- }
- if (window->widthincr != GLFW_DONT_CARE &&
- window->heightincr != GLFW_DONT_CARE && !window->x11.maximized)
- {
- hints->flags |= PResizeInc;
- hints->width_inc = window->widthincr;
- hints->height_inc = window->heightincr;
- }
- }
- else
- {
- hints->flags |= (PMinSize | PMaxSize);
- hints->min_width = hints->max_width = width;
- hints->min_height = hints->max_height = height;
- }
- }
- hints->flags |= PWinGravity;
- hints->win_gravity = StaticGravity;
- XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints);
- XFree(hints);
- }
- static bool
- is_window_fullscreen(_GLFWwindow* window)
- {
- Atom* states;
- unsigned long i;
- bool ans = false;
- if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_FULLSCREEN)
- return ans;
- const unsigned long count =
- _glfwGetWindowPropertyX11(window->x11.handle,
- _glfw.x11.NET_WM_STATE,
- XA_ATOM,
- (unsigned char**) &states);
- for (i = 0; i < count; i++)
- {
- if (states[i] == _glfw.x11.NET_WM_STATE_FULLSCREEN)
- {
- ans = true;
- break;
- }
- }
- if (states)
- XFree(states);
- return ans;
- }
- static void
- set_fullscreen(_GLFWwindow *window, bool on) {
- if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN) {
- sendEventToWM(window,
- _glfw.x11.NET_WM_STATE,
- on ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE,
- _glfw.x11.NET_WM_STATE_FULLSCREEN,
- 0, 1, 0);
- // Enable compositor bypass
- if (!window->x11.transparent)
- {
- if (on) {
- const unsigned long value = 1;
- XChangeProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32,
- PropModeReplace, (unsigned char*) &value, 1);
- } else {
- XDeleteProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.NET_WM_BYPASS_COMPOSITOR);
- }
- }
- } else {
- static bool warned = false;
- if (!warned) {
- warned = true;
- _glfwInputErrorX11(GLFW_PLATFORM_ERROR,
- "X11: Failed to toggle fullscreen, the window manager does not support it");
- }
- }
- }
- bool
- _glfwPlatformIsFullscreen(_GLFWwindow *window, unsigned int flags UNUSED) {
- return is_window_fullscreen(window);
- }
- bool
- _glfwPlatformToggleFullscreen(_GLFWwindow *window, unsigned int flags UNUSED) {
- bool already_fullscreen = is_window_fullscreen(window);
- set_fullscreen(window, !already_fullscreen);
- return !already_fullscreen;
- }
- // Updates the full screen status of the window
- //
- static void updateWindowMode(_GLFWwindow* window)
- {
- if (window->monitor)
- {
- if (_glfw.x11.xinerama.available &&
- _glfw.x11.NET_WM_FULLSCREEN_MONITORS)
- {
- sendEventToWM(window,
- _glfw.x11.NET_WM_FULLSCREEN_MONITORS,
- window->monitor->x11.index,
- window->monitor->x11.index,
- window->monitor->x11.index,
- window->monitor->x11.index,
- 0);
- }
- set_fullscreen(window, true);
- }
- else
- {
- if (_glfw.x11.xinerama.available &&
- _glfw.x11.NET_WM_FULLSCREEN_MONITORS)
- {
- XDeleteProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.NET_WM_FULLSCREEN_MONITORS);
- }
- set_fullscreen(window, false);
- }
- }
- // Encode a Unicode code point to a UTF-8 stream
- // Based on cutef8 by Jeff Bezanson (Public Domain)
- //
- static size_t encodeUTF8(char* s, unsigned int ch)
- {
- size_t count = 0;
- if (ch < 0x80)
- s[count++] = (char) ch;
- else if (ch < 0x800)
- {
- s[count++] = (ch >> 6) | 0xc0;
- s[count++] = (ch & 0x3f) | 0x80;
- }
- else if (ch < 0x10000)
- {
- s[count++] = (ch >> 12) | 0xe0;
- s[count++] = ((ch >> 6) & 0x3f) | 0x80;
- s[count++] = (ch & 0x3f) | 0x80;
- }
- else if (ch < 0x110000)
- {
- s[count++] = (ch >> 18) | 0xf0;
- s[count++] = ((ch >> 12) & 0x3f) | 0x80;
- s[count++] = ((ch >> 6) & 0x3f) | 0x80;
- s[count++] = (ch & 0x3f) | 0x80;
- }
- return count;
- }
- // Convert the specified Latin-1 string to UTF-8
- //
- static char* convertLatin1toUTF8(const char* source)
- {
- size_t size = 1;
- const char* sp;
- if (source) {
- for (sp = source; *sp; sp++)
- size += (*sp & 0x80) ? 2 : 1;
- }
- char* target = calloc(size, 1);
- char* tp = target;
- if (source) {
- for (sp = source; *sp; sp++)
- tp += encodeUTF8(tp, *sp);
- }
- return target;
- }
- // Updates the cursor image according to its cursor mode
- //
- static void updateCursorImage(_GLFWwindow* window)
- {
- if (window->cursorMode == GLFW_CURSOR_NORMAL)
- {
- if (window->cursor)
- {
- XDefineCursor(_glfw.x11.display, window->x11.handle,
- window->cursor->x11.handle);
- }
- else
- XUndefineCursor(_glfw.x11.display, window->x11.handle);
- }
- else
- {
- XDefineCursor(_glfw.x11.display, window->x11.handle,
- _glfw.x11.hiddenCursorHandle);
- }
- }
- // Enable XI2 raw mouse motion events
- //
- static void enableRawMouseMotion(_GLFWwindow* window UNUSED)
- {
- XIEventMask em;
- unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 };
- em.deviceid = XIAllMasterDevices;
- em.mask_len = sizeof(mask);
- em.mask = mask;
- XISetMask(mask, XI_RawMotion);
- XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);
- }
- // Disable XI2 raw mouse motion events
- //
- static void disableRawMouseMotion(_GLFWwindow* window UNUSED)
- {
- XIEventMask em;
- unsigned char mask[] = { 0 };
- em.deviceid = XIAllMasterDevices;
- em.mask_len = sizeof(mask);
- em.mask = mask;
- XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);
- }
- // Apply disabled cursor mode to a focused window
- //
- static void disableCursor(_GLFWwindow* window)
- {
- if (window->rawMouseMotion)
- enableRawMouseMotion(window);
- _glfw.x11.disabledCursorWindow = window;
- _glfwPlatformGetCursorPos(window,
- &_glfw.x11.restoreCursorPosX,
- &_glfw.x11.restoreCursorPosY);
- updateCursorImage(window);
- _glfwCenterCursorInContentArea(window);
- XGrabPointer(_glfw.x11.display, window->x11.handle, True,
- ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
- GrabModeAsync, GrabModeAsync,
- window->x11.handle,
- _glfw.x11.hiddenCursorHandle,
- CurrentTime);
- }
- // Exit disabled cursor mode for the specified window
- //
- static void enableCursor(_GLFWwindow* window)
- {
- if (window->rawMouseMotion)
- disableRawMouseMotion(window);
- _glfw.x11.disabledCursorWindow = NULL;
- XUngrabPointer(_glfw.x11.display, CurrentTime);
- _glfwPlatformSetCursorPos(window,
- _glfw.x11.restoreCursorPosX,
- _glfw.x11.restoreCursorPosY);
- updateCursorImage(window);
- }
- // Create the X11 window (and its colormap)
- //
- static bool createNativeWindow(_GLFWwindow* window,
- const _GLFWwndconfig* wndconfig,
- Visual* visual, int depth)
- {
- int width = wndconfig->width;
- int height = wndconfig->height;
- if (wndconfig->scaleToMonitor)
- {
- width *= (int)_glfw.x11.contentScaleX;
- height *= (int)_glfw.x11.contentScaleY;
- }
- // Create a colormap based on the visual used by the current context
- window->x11.colormap = XCreateColormap(_glfw.x11.display,
- _glfw.x11.root,
- visual,
- AllocNone);
- window->x11.transparent = _glfwIsVisualTransparentX11(visual);
- XSetWindowAttributes wa = { 0 };
- wa.colormap = window->x11.colormap;
- wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask |
- PointerMotionMask | ButtonPressMask | ButtonReleaseMask |
- ExposureMask | FocusChangeMask | VisibilityChangeMask |
- EnterWindowMask | LeaveWindowMask | PropertyChangeMask;
- _glfwGrabErrorHandlerX11();
- window->x11.parent = _glfw.x11.root;
- window->x11.handle = XCreateWindow(_glfw.x11.display,
- _glfw.x11.root,
- 0, 0, // Position
- width, height,
- 0, // Border width
- depth, // Color depth
- InputOutput,
- visual,
- CWBorderPixel | CWColormap | CWEventMask,
- &wa);
- _glfwReleaseErrorHandlerX11();
- if (!window->x11.handle)
- {
- _glfwInputErrorX11(GLFW_PLATFORM_ERROR,
- "X11: Failed to create window");
- return false;
- }
- XSaveContext(_glfw.x11.display,
- window->x11.handle,
- _glfw.x11.context,
- (XPointer) window);
- if (!wndconfig->decorated)
- _glfwPlatformSetWindowDecorated(window, false);
- if (_glfw.x11.NET_WM_STATE && !window->monitor)
- {
- Atom states[3];
- int count = 0;
- if (wndconfig->floating)
- {
- if (_glfw.x11.NET_WM_STATE_ABOVE)
- states[count++] = _glfw.x11.NET_WM_STATE_ABOVE;
- }
- if (wndconfig->maximized)
- {
- if (_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
- _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
- {
- states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT;
- states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ;
- window->x11.maximized = true;
- }
- }
- if (count)
- {
- XChangeProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
- PropModeReplace, (unsigned char*) states, count);
- }
- }
- // Declare the WM protocols supported by GLFW
- {
- Atom protocols[] =
- {
- _glfw.x11.WM_DELETE_WINDOW,
- _glfw.x11.NET_WM_PING
- };
- XSetWMProtocols(_glfw.x11.display, window->x11.handle,
- protocols, sizeof(protocols) / sizeof(Atom));
- }
- // Declare our PID
- {
- const long pid = getpid();
- XChangeProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.NET_WM_PID, XA_CARDINAL, 32,
- PropModeReplace,
- (unsigned char*) &pid, 1);
- }
- if (_glfw.x11.NET_WM_WINDOW_TYPE && _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL)
- {
- Atom type = _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL;
- XChangeProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.NET_WM_WINDOW_TYPE, XA_ATOM, 32,
- PropModeReplace, (unsigned char*) &type, 1);
- }
- // Set ICCCM WM_HINTS property
- {
- XWMHints* hints = XAllocWMHints();
- if (!hints)
- {
- _glfwInputError(GLFW_OUT_OF_MEMORY,
- "X11: Failed to allocate WM hints");
- return false;
- }
- hints->flags = StateHint;
- hints->initial_state = NormalState;
- XSetWMHints(_glfw.x11.display, window->x11.handle, hints);
- XFree(hints);
- }
- updateNormalHints(window, width, height);
- // Set ICCCM WM_CLASS property
- {
- XClassHint* hint = XAllocClassHint();
- if (strlen(wndconfig->x11.instanceName) &&
- strlen(wndconfig->x11.className))
- {
- hint->res_name = (char*) wndconfig->x11.instanceName;
- hint->res_class = (char*) wndconfig->x11.className;
- }
- else
- {
- const char* resourceName = getenv("RESOURCE_NAME");
- if (resourceName && strlen(resourceName))
- hint->res_name = (char*) resourceName;
- else if (strlen(wndconfig->title))
- hint->res_name = (char*) wndconfig->title;
- else
- hint->res_name = (char*) "glfw-application";
- if (strlen(wndconfig->title))
- hint->res_class = (char*) wndconfig->title;
- else
- hint->res_class = (char*) "GLFW-Application";
- }
- XSetClassHint(_glfw.x11.display, window->x11.handle, hint);
- XFree(hint);
- }
- // Announce support for Xdnd (drag and drop)
- {
- const Atom version = _GLFW_XDND_VERSION;
- XChangeProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.XdndAware, XA_ATOM, 32,
- PropModeReplace, (unsigned char*) &version, 1);
- }
- _glfwPlatformSetWindowTitle(window, wndconfig->title);
- _glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos);
- _glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height);
- if (_glfw.hints.window.blur_radius > 0) _glfwPlatformSetWindowBlur(window, _glfw.hints.window.blur_radius);
- return true;
- }
- static size_t
- get_clipboard_data(const _GLFWClipboardData *cd, const char *mime, char **data) {
- *data = NULL;
- if (cd->get_data == NULL) { return 0; }
- GLFWDataChunk chunk = cd->get_data(mime, NULL, cd->ctype);
- char *buf = NULL;
- size_t sz = 0, cap = 0;
- void *iter = chunk.iter;
- if (!iter) return 0;
- while (true) {
- chunk = cd->get_data(mime, iter, cd->ctype);
- if (!chunk.sz) break;
- if (cap < sz + chunk.sz) {
- cap = MAX(cap * 2, sz + 4 * chunk.sz);
- buf = realloc(buf, cap * sizeof(buf[0]));
- }
- memcpy(buf + sz, chunk.data, chunk.sz);
- sz += chunk.sz;
- if (chunk.free) chunk.free((void*)chunk.free_data);
- }
- *data = buf;
- cd->get_data(NULL, iter, cd->ctype);
- return sz;
- }
- static void
- get_atom_names(const Atom *atoms, int count, char **atom_names) {
- _glfwGrabErrorHandlerX11();
- XGetAtomNames(_glfw.x11.display, (Atom*)atoms, count, atom_names);
- _glfwReleaseErrorHandlerX11();
- if (_glfw.x11.errorCode != Success) {
- for (int i = 0; i < count; i++) {
- _glfwGrabErrorHandlerX11();
- atom_names[i] = XGetAtomName(_glfw.x11.display, atoms[i]);
- _glfwReleaseErrorHandlerX11();
- if (_glfw.x11.errorCode != Success) atom_names[i] = NULL;
- }
- }
- }
- // Set the specified property to the selection converted to the requested target
- //
- static Atom writeTargetToProperty(const XSelectionRequestEvent* request)
- {
- const AtomArray *aa;
- const _GLFWClipboardData *cd;
- if (request->selection == _glfw.x11.PRIMARY) {
- aa = &_glfw.x11.primary_atoms;
- cd = &_glfw.primary;
- } else {
- aa = &_glfw.x11.clipboard_atoms;
- cd = &_glfw.clipboard;
- }
- if (request->property == None)
- {
- // The requester is a legacy client (ICCCM section 2.2)
- // We don't support legacy clients, so fail here
- return None;
- }
- if (request->target == _glfw.x11.TARGETS)
- {
- // The list of supported targets was requested
- Atom *targets = calloc(aa->sz + 2, sizeof(Atom));
- targets[0] = _glfw.x11.TARGETS;
- targets[1] = _glfw.x11.MULTIPLE;
- for (size_t i = 0; i < aa->sz; i++) targets[i+2] = aa->array[i].atom;
- XChangeProperty(_glfw.x11.display,
- request->requestor,
- request->property,
- XA_ATOM,
- 32,
- PropModeReplace,
- (unsigned char*) targets,
- aa->sz + 2);
- free(targets);
- return request->property;
- }
- if (request->target == _glfw.x11.MULTIPLE)
- {
- // Multiple conversions were requested
- Atom* targets;
- size_t i, j, count;
- count = _glfwGetWindowPropertyX11(request->requestor,
- request->property,
- _glfw.x11.ATOM_PAIR,
- (unsigned char**) &targets);
- for (i = 0; i < count; i += 2)
- {
- for (j = 0; j < aa->sz; j++)
- {
- if (targets[i] == aa->array[j].atom)
- break;
- }
- if (j < aa->sz)
- {
- char *data = NULL; size_t sz = get_clipboard_data(cd, aa->array[j].mime, &data);
- if (data) XChangeProperty(_glfw.x11.display,
- request->requestor,
- targets[i + 1],
- targets[i],
- 8,
- PropModeReplace,
- (unsigned char *) data,
- sz);
- free(data);
- }
- else
- targets[i + 1] = None;
- }
- XChangeProperty(_glfw.x11.display,
- request->requestor,
- request->property,
- _glfw.x11.ATOM_PAIR,
- 32,
- PropModeReplace,
- (unsigned char*) targets,
- count);
- XFree(targets);
- return request->property;
- }
- if (request->target == _glfw.x11.SAVE_TARGETS)
- {
- // The request is a check whether we support SAVE_TARGETS
- // It should be handled as a no-op side effect target
- XChangeProperty(_glfw.x11.display,
- request->requestor,
- request->property,
- _glfw.x11.NULL_,
- 32,
- PropModeReplace,
- NULL,
- 0);
- return request->property;
- }
- // Conversion to a data target was requested
- for (size_t i = 0; i < aa->sz; i++)
- {
- if (request->target == aa->array[i].atom)
- {
- // The requested target is one we support
- char *data = NULL; size_t sz = get_clipboard_data(cd, aa->array[i].mime, &data);
- if (data) XChangeProperty(_glfw.x11.display,
- request->requestor,
- request->property,
- request->target,
- 8,
- PropModeReplace,
- (unsigned char *) data,
- sz);
- free(data);
- return request->property;
- }
- }
- // The requested target is not supported
- return None;
- }
- static void handleSelectionClear(XEvent* event)
- {
- if (event->xselectionclear.selection == _glfw.x11.PRIMARY)
- {
- _glfw_free_clipboard_data(&_glfw.primary);
- }
- else
- {
- _glfw_free_clipboard_data(&_glfw.clipboard);
- }
- }
- static void handleSelectionRequest(XEvent* event)
- {
- const XSelectionRequestEvent* request = &event->xselectionrequest;
- XEvent reply = { SelectionNotify };
- reply.xselection.property = writeTargetToProperty(request);
- reply.xselection.display = request->display;
- reply.xselection.requestor = request->requestor;
- reply.xselection.selection = request->selection;
- reply.xselection.target = request->target;
- reply.xselection.time = request->time;
- XSendEvent(_glfw.x11.display, request->requestor, False, 0, &reply);
- }
- static void
- getSelectionString(Atom selection, Atom *targets, size_t num_targets, GLFWclipboardwritedatafun write_data, void *object, bool report_not_found)
- {
- #define XFREE(x) { if (x) XFree(x); x = NULL; }
- if (XGetSelectionOwner(_glfw.x11.display, selection) == _glfw.x11.helperWindowHandle) {
- write_data(object, NULL, 1);
- return;
- }
- bool found = false;
- for (size_t i = 0; !found && i < num_targets; i++)
- {
- char* data = NULL;
- Atom actualType = None;
- int actualFormat = 0;
- unsigned long itemCount = 0, bytesAfter = 0;
- monotonic_t start = glfwGetTime();
- XEvent notification, dummy;
- XConvertSelection(_glfw.x11.display,
- selection,
- targets[i],
- _glfw.x11.GLFW_SELECTION,
- _glfw.x11.helperWindowHandle,
- CurrentTime);
- while (!XCheckTypedWindowEvent(_glfw.x11.display,
- _glfw.x11.helperWindowHandle,
- SelectionNotify,
- ¬ification))
- {
- monotonic_t time = glfwGetTime();
- if (time - start > s_to_monotonic_t(2ll)) return;
- waitForX11Event(s_to_monotonic_t(2ll) - (time - start));
- }
- if (notification.xselection.property == None)
- continue;
- XCheckIfEvent(_glfw.x11.display,
- &dummy,
- isSelPropNewValueNotify,
- (XPointer) ¬ification);
- XGetWindowProperty(_glfw.x11.display,
- notification.xselection.requestor,
- notification.xselection.property,
- 0,
- LONG_MAX,
- True,
- AnyPropertyType,
- &actualType,
- &actualFormat,
- &itemCount,
- &bytesAfter,
- (unsigned char**) &data);
- if (actualType == _glfw.x11.INCR)
- {
- for (;;)
- {
- start = glfwGetTime();
- while (!XCheckIfEvent(_glfw.x11.display,
- &dummy,
- isSelPropNewValueNotify,
- (XPointer) ¬ification))
- {
- monotonic_t time = glfwGetTime();
- if (time - start > s_to_monotonic_t(2ll)) {
- return;
- }
- waitForX11Event(s_to_monotonic_t(2ll) - (time - start));
- }
- XFREE(data);
- XGetWindowProperty(_glfw.x11.display,
- notification.xselection.requestor,
- notification.xselection.property,
- 0,
- LONG_MAX,
- True,
- AnyPropertyType,
- &actualType,
- &actualFormat,
- &itemCount,
- &bytesAfter,
- (unsigned char**) &data);
- if (itemCount)
- {
- const char *string = data;
- if (targets[i] == XA_STRING) {
- string = convertLatin1toUTF8(data);
- itemCount = strlen(string);
- }
- bool ok = write_data(object, string, itemCount);
- if (string != data) free((void*)string);
- if (!ok) { XFREE(data); break; }
- } else { found = true; break; }
- }
- }
- else if (actualType == targets[i])
- {
- if (targets[i] == XA_STRING) {
- const char *string = convertLatin1toUTF8(data);
- write_data(object, string, strlen(string)); free((void*)string);
- } else write_data(object, data, itemCount);
- found = true;
- }
- else if (actualType == XA_ATOM && targets[i] == _glfw.x11.TARGETS) {
- found = true;
- write_data(object, data, sizeof(Atom) * itemCount);
- }
- XFREE(data);
- }
- if (!found && report_not_found)
- {
- _glfwInputError(GLFW_FORMAT_UNAVAILABLE,
- "X11: Failed to convert selection to data from clipboard");
- }
- #undef XFREE
- }
- // Make the specified window and its video mode active on its monitor
- //
- static void acquireMonitor(_GLFWwindow* window)
- {
- if (_glfw.x11.saver.count == 0)
- {
- // Remember old screen saver settings
- XGetScreenSaver(_glfw.x11.display,
- &_glfw.x11.saver.timeout,
- &_glfw.x11.saver.interval,
- &_glfw.x11.saver.blanking,
- &_glfw.x11.saver.exposure);
- // Disable screen saver
- XSetScreenSaver(_glfw.x11.display, 0, 0, DontPreferBlanking,
- DefaultExposures);
- }
- if (!window->monitor->window)
- _glfw.x11.saver.count++;
- _glfwSetVideoModeX11(window->monitor, &window->videoMode);
- _glfwInputMonitorWindow(window->monitor, window);
- }
- // Remove the window and restore the original video mode
- //
- static void releaseMonitor(_GLFWwindow* window)
- {
- if (window->monitor->window != window)
- return;
- _glfwInputMonitorWindow(window->monitor, NULL);
- _glfwRestoreVideoModeX11(window->monitor);
- _glfw.x11.saver.count--;
- if (_glfw.x11.saver.count == 0)
- {
- // Restore old screen saver settings
- XSetScreenSaver(_glfw.x11.display,
- _glfw.x11.saver.timeout,
- _glfw.x11.saver.interval,
- _glfw.x11.saver.blanking,
- _glfw.x11.saver.exposure);
- }
- }
- static void onConfigChange(void)
- {
- float xscale, yscale;
- _glfwGetSystemContentScaleX11(&xscale, &yscale, true);
- if (xscale != _glfw.x11.contentScaleX || yscale != _glfw.x11.contentScaleY)
- {
- _GLFWwindow* window = _glfw.windowListHead;
- _glfw.x11.contentScaleX = xscale;
- _glfw.x11.contentScaleY = yscale;
- while (window)
- {
- _glfwInputWindowContentScale(window, xscale, yscale);
- window = window->next;
- }
- }
- }
- // Process the specified X event
- //
- static void processEvent(XEvent *event)
- {
- static bool keymap_dirty = false;
- #define UPDATE_KEYMAP_IF_NEEDED if (keymap_dirty) { keymap_dirty = false; glfw_xkb_compile_keymap(&_glfw.x11.xkb, NULL); }
- if (_glfw.x11.randr.available)
- {
- if (event->type == _glfw.x11.randr.eventBase + RRNotify)
- {
- XRRUpdateConfiguration(event);
- _glfwPollMonitorsX11();
- return;
- }
- }
- if (event->type == PropertyNotify &&
- event->xproperty.window == _glfw.x11.root &&
- event->xproperty.atom == _glfw.x11.RESOURCE_MANAGER)
- {
- onConfigChange();
- return;
- }
- if (event->type == GenericEvent)
- {
- if (_glfw.x11.xi.available)
- {
- _GLFWwindow* window = _glfw.x11.disabledCursorWindow;
- if (window &&
- window->rawMouseMotion &&
- event->xcookie.extension == _glfw.x11.xi.majorOpcode &&
- XGetEventData(_glfw.x11.display, &event->xcookie) &&
- event->xcookie.evtype == XI_RawMotion)
- {
- XIRawEvent* re = event->xcookie.data;
- if (re->valuators.mask_len)
- {
- const double* values = re->raw_values;
- double xpos = window->virtualCursorPosX;
- double ypos = window->virtualCursorPosY;
- if (XIMaskIsSet(re->valuators.mask, 0))
- {
- xpos += *values;
- values++;
- }
- if (XIMaskIsSet(re->valuators.mask, 1))
- ypos += *values;
- _glfwInputCursorPos(window, xpos, ypos);
- }
- }
- XFreeEventData(_glfw.x11.display, &event->xcookie);
- }
- return;
- }
- if (event->type == SelectionClear)
- {
- handleSelectionClear(event);
- return;
- }
- else if (event->type == SelectionRequest)
- {
- handleSelectionRequest(event);
- return;
- }
- else if (event->type == _glfw.x11.xkb.eventBase)
- {
- XkbEvent *kb_event = (XkbEvent*)event;
- if (kb_event->any.device != (unsigned int)_glfw.x11.xkb.keyboard_device_id) return;
- switch(kb_event->any.xkb_type) {
- case XkbNewKeyboardNotify: {
- XkbNewKeyboardNotifyEvent *newkb_event = (XkbNewKeyboardNotifyEvent*)kb_event;
- if (_glfw.hints.init.debugKeyboard) printf(
- "Got XkbNewKeyboardNotify event with changes: key codes: %d geometry: %d device id: %d\n",
- !!(newkb_event->changed & XkbNKN_KeycodesMask), !!(newkb_event->changed & XkbNKN_GeometryMask),
- !!(newkb_event->changed & XkbNKN_DeviceIDMask));
- if (newkb_event->changed & XkbNKN_DeviceIDMask) {
- keymap_dirty = true;
- if (!glfw_xkb_update_x11_keyboard_id(&_glfw.x11.xkb)) return;
- }
- if (newkb_event->changed & XkbNKN_KeycodesMask) {
- keymap_dirty = true;
- }
- return;
- }
- case XkbMapNotify:
- {
- if (_glfw.hints.init.debugKeyboard) printf("Got XkbMapNotify event, keymaps will be reloaded\n");
- keymap_dirty = true;
- return;
- }
- case XkbStateNotify:
- {
- UPDATE_KEYMAP_IF_NEEDED;
- XkbStateNotifyEvent *state_event = (XkbStateNotifyEvent*)kb_event;
- glfw_xkb_update_modifiers(
- &_glfw.x11.xkb, state_event->base_mods, state_event->latched_mods,
- state_event->locked_mods, state_event->base_group, state_event->latched_group, state_event->locked_group
- );
- return;
- }
- }
- return;
- }
- _GLFWwindow* window = NULL;
- if (XFindContext(_glfw.x11.display,
- event->xany.window,
- _glfw.x11.context,
- (XPointer*) &window) != 0)
- {
- // This is an event for a window that has already been destroyed
- return;
- }
- switch (event->type)
- {
- case ReparentNotify:
- {
- window->x11.parent = event->xreparent.parent;
- return;
- }
- case KeyPress:
- {
- UPDATE_KEYMAP_IF_NEEDED;
- glfw_xkb_handle_key_event(window, &_glfw.x11.xkb, event->xkey.keycode, GLFW_PRESS);
- return;
- }
- case KeyRelease:
- {
- UPDATE_KEYMAP_IF_NEEDED;
- if (!_glfw.x11.xkb.detectable)
- {
- // HACK: Key repeat events will arrive as KeyRelease/KeyPress
- // pairs with similar or identical time stamps
- // The key repeat logic in _glfwInputKey expects only key
- // presses to repeat, so detect and discard release events
- if (XEventsQueued(_glfw.x11.display, QueuedAfterReading))
- {
- XEvent next;
- XPeekEvent(_glfw.x11.display, &next);
- if (next.type == KeyPress &&
- next.xkey.window == event->xkey.window &&
- next.xkey.keycode == event->xkey.keycode)
- {
- // HACK: The time of repeat events sometimes doesn't
- // match that of the press event, so add an
- // epsilon
- // Toshiyuki Takahashi can press a button
- // 16 times per second so it's fairly safe to
- // assume that no human is pressing the key 50
- // times per second (value is ms)
- if ((next.xkey.time - event->xkey.time) < 20)
- {
- // This is very likely a server-generated key repeat
- // event, so ignore it
- return;
- }
- }
- }
- }
- glfw_xkb_handle_key_event(window, &_glfw.x11.xkb, event->xkey.keycode, GLFW_RELEASE);
- return;
- }
- case ButtonPress:
- {
- const int mods = translateState(event->xbutton.state);
- if (event->xbutton.button == Button1)
- _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, mods);
- else if (event->xbutton.button == Button2)
- _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, mods);
- else if (event->xbutton.button == Button3)
- _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS, mods);
- // Modern X provides scroll events as mouse button presses
- else if (event->xbutton.button == Button4)
- _glfwInputScroll(window, 0.0, 1.0, 0, mods);
- else if (event->xbutton.button == Button5)
- _glfwInputScroll(window, 0.0, -1.0, 0, mods);
- else if (event->xbutton.button == Button6)
- _glfwInputScroll(window, 1.0, 0.0, 0, mods);
- else if (event->xbutton.button == Button7)
- _glfwInputScroll(window, -1.0, 0.0, 0, mods);
- else
- {
- // Additional buttons after 7 are treated as regular buttons
- // We subtract 4 to fill the gap left by scroll input above
- _glfwInputMouseClick(window,
- event->xbutton.button - Button1 - 4,
- GLFW_PRESS,
- mods);
- }
- return;
- }
- case ButtonRelease:
- {
- const int mods = translateState(event->xbutton.state);
- if (event->xbutton.button == Button1)
- {
- _glfwInputMouseClick(window,
- GLFW_MOUSE_BUTTON_LEFT,
- GLFW_RELEASE,
- mods);
- }
- else if (event->xbutton.button == Button2)
- {
- _glfwInputMouseClick(window,
- GLFW_MOUSE_BUTTON_MIDDLE,
- GLFW_RELEASE,
- mods);
- }
- else if (event->xbutton.button == Button3)
- {
- _glfwInputMouseClick(window,
- GLFW_MOUSE_BUTTON_RIGHT,
- GLFW_RELEASE,
- mods);
- }
- else if (event->xbutton.button > Button7)
- {
- // Additional buttons after 7 are treated as regular buttons
- // We subtract 4 to fill the gap left by scroll input above
- _glfwInputMouseClick(window,
- event->xbutton.button - Button1 - 4,
- GLFW_RELEASE,
- mods);
- }
- return;
- }
- case EnterNotify:
- {
- // XEnterWindowEvent is XCrossingEvent
- const int x = event->xcrossing.x;
- const int y = event->xcrossing.y;
- // HACK: This is a workaround for WMs (KWM, Fluxbox) that otherwise
- // ignore the defined cursor for hidden cursor mode
- if (window->cursorMode == GLFW_CURSOR_HIDDEN)
- updateCursorImage(window);
- _glfwInputCursorEnter(window, true);
- _glfwInputCursorPos(window, x, y);
- window->x11.lastCursorPosX = x;
- window->x11.lastCursorPosY = y;
- return;
- }
- case LeaveNotify:
- {
- _glfwInputCursorEnter(window, false);
- return;
- }
- case MotionNotify:
- {
- const int x = event->xmotion.x;
- const int y = event->xmotion.y;
- if (x != window->x11.warpCursorPosX ||
- y != window->x11.warpCursorPosY)
- {
- // The cursor was moved by something other than GLFW
- if (window->cursorMode == GLFW_CURSOR_DISABLED)
- {
- if (_glfw.x11.disabledCursorWindow != window)
- return;
- if (window->rawMouseMotion)
- return;
- const int dx = x - window->x11.lastCursorPosX;
- const int dy = y - window->x11.lastCursorPosY;
- _glfwInputCursorPos(window,
- window->virtualCursorPosX + dx,
- window->virtualCursorPosY + dy);
- }
- else
- _glfwInputCursorPos(window, x, y);
- }
- window->x11.lastCursorPosX = x;
- window->x11.lastCursorPosY = y;
- return;
- }
- case ConfigureNotify:
- {
- if (event->xconfigure.width != window->x11.width ||
- event->xconfigure.height != window->x11.height)
- {
- _glfwInputFramebufferSize(window,
- event->xconfigure.width,
- event->xconfigure.height);
- _glfwInputWindowSize(window,
- event->xconfigure.width,
- event->xconfigure.height);
- window->x11.width = event->xconfigure.width;
- window->x11.height = event->xconfigure.height;
- }
- int xpos = event->xconfigure.x;
- int ypos = event->xconfigure.y;
- // NOTE: ConfigureNotify events from the server are in local
- // coordinates, so if we are reparented we need to translate
- // the position into root (screen) coordinates
- if (!event->xany.send_event && window->x11.parent != _glfw.x11.root)
- {
- Window dummy;
- _glfwGrabErrorHandlerX11();
- XTranslateCoordinates(_glfw.x11.display,
- window->x11.parent,
- _glfw.x11.root,
- xpos, ypos,
- &xpos, &ypos,
- &dummy);
- _glfwReleaseErrorHandlerX11();
- if (_glfw.x11.errorCode != Success) {
- _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to translate ConfigureNotiy co-ords for reparented window");
- return;
- }
- }
- if (xpos != window->x11.xpos || ypos != window->x11.ypos)
- {
- _glfwInputWindowPos(window, xpos, ypos);
- window->x11.xpos = xpos;
- window->x11.ypos = ypos;
- }
- return;
- }
- case ClientMessage:
- {
- // Custom client message, probably from the window manager
- if (event->xclient.message_type == None)
- return;
- if (event->xclient.message_type == _glfw.x11.WM_PROTOCOLS)
- {
- const Atom protocol = event->xclient.data.l[0];
- if (protocol == None)
- return;
- if (protocol == _glfw.x11.WM_DELETE_WINDOW)
- {
- // The window manager was asked to close the window, for
- // example by the user pressing a 'close' window decoration
- // button
- _glfwInputWindowCloseRequest(window);
- }
- else if (protocol == _glfw.x11.NET_WM_PING)
- {
- // The window manager is pinging the application to ensure
- // it's still responding to events
- XEvent reply = *event;
- reply.xclient.window = _glfw.x11.root;
- XSendEvent(_glfw.x11.display, _glfw.x11.root,
- False,
- SubstructureNotifyMask | SubstructureRedirectMask,
- &reply);
- }
- }
- else if (event->xclient.message_type == _glfw.x11.XdndEnter)
- {
- // A drag operation has entered the window
- unsigned long i, count;
- Atom* formats = NULL;
- const bool list = event->xclient.data.l[1] & 1;
- _glfw.x11.xdnd.source = event->xclient.data.l[0];
- _glfw.x11.xdnd.version = event->xclient.data.l[1] >> 24;
- memset(_glfw.x11.xdnd.format, 0, sizeof(_glfw.x11.xdnd.format));
- _glfw.x11.xdnd.format_priority = 0;
- if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
- return;
- if (list)
- {
- count = _glfwGetWindowPropertyX11(_glfw.x11.xdnd.source,
- _glfw.x11.XdndTypeList,
- XA_ATOM,
- (unsigned char**) &formats);
- }
- else
- {
- count = 3;
- formats = (Atom*) event->xclient.data.l + 2;
- }
- char **atom_names = calloc(count, sizeof(char*));
- if (atom_names) {
- get_atom_names(formats, count, atom_names);
- for (i = 0; i < count; i++)
- {
- if (atom_names[i]) {
- int prio = _glfwInputDrop(window, atom_names[i], NULL, 0);
- if (prio > _glfw.x11.xdnd.format_priority) {
- _glfw.x11.xdnd.format_priority = prio;
- strncpy(_glfw.x11.xdnd.format, atom_names[i], arraysz(_glfw.x11.xdnd.format) - 1);
- }
- XFree(atom_names[i]);
- }
- }
- free(atom_names);
- }
- if (list && formats)
- XFree(formats);
- }
- else if (event->xclient.message_type == _glfw.x11.XdndDrop)
- {
- // The drag operation has finished by dropping on the window
- Time time = CurrentTime;
- if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
- return;
- if (_glfw.x11.xdnd.format_priority > 0)
- {
- if (_glfw.x11.xdnd.version >= 1)
- time = event->xclient.data.l[2];
- // Request the chosen format from the source window
- XConvertSelection(_glfw.x11.display,
- _glfw.x11.XdndSelection,
- XInternAtom(_glfw.x11.display, _glfw.x11.xdnd.format, 0),
- _glfw.x11.XdndSelection,
- window->x11.handle,
- time);
- }
- else if (_glfw.x11.xdnd.version >= 2)
- {
- XEvent reply = { ClientMessage };
- reply.xclient.window = _glfw.x11.xdnd.source;
- reply.xclient.message_type = _glfw.x11.XdndFinished;
- reply.xclient.format = 32;
- reply.xclient.data.l[0] = window->x11.handle;
- reply.xclient.data.l[1] = 0; // The drag was rejected
- reply.xclient.data.l[2] = None;
- XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
- False, NoEventMask, &reply);
- XFlush(_glfw.x11.display);
- }
- }
- else if (event->xclient.message_type == _glfw.x11.XdndPosition)
- {
- // The drag operation has moved over the window
- const int xabs = (event->xclient.data.l[2] >> 16) & 0xffff;
- const int yabs = (event->xclient.data.l[2]) & 0xffff;
- Window dummy;
- int xpos = 0, ypos = 0;
- if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION)
- return;
- _glfwGrabErrorHandlerX11();
- XTranslateCoordinates(_glfw.x11.display,
- _glfw.x11.root,
- window->x11.handle,
- xabs, yabs,
- &xpos, &ypos,
- &dummy);
- _glfwReleaseErrorHandlerX11();
- if (_glfw.x11.errorCode != Success)
- _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to get DND event position");
- _glfwInputCursorPos(window, xpos, ypos);
- XEvent reply = { ClientMessage };
- reply.xclient.window = _glfw.x11.xdnd.source;
- reply.xclient.message_type = _glfw.x11.XdndStatus;
- reply.xclient.format = 32;
- reply.xclient.data.l[0] = window->x11.handle;
- reply.xclient.data.l[2] = 0; // Specify an empty rectangle
- reply.xclient.data.l[3] = 0;
- if (_glfw.x11.xdnd.format_priority > 0)
- {
- // Reply that we are ready to copy the dragged data
- reply.xclient.data.l[1] = 1; // Accept with no rectangle
- if (_glfw.x11.xdnd.version >= 2)
- reply.xclient.data.l[4] = _glfw.x11.XdndActionCopy;
- }
- XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
- False, NoEventMask, &reply);
- XFlush(_glfw.x11.display);
- }
- return;
- }
- case SelectionNotify:
- {
- if (event->xselection.property == _glfw.x11.XdndSelection)
- {
- // The converted data from the drag operation has arrived
- char* data;
- const unsigned long result =
- _glfwGetWindowPropertyX11(event->xselection.requestor,
- event->xselection.property,
- event->xselection.target,
- (unsigned char**) &data);
- if (result)
- {
- _glfwInputDrop(window, _glfw.x11.xdnd.format, data, result);
- }
- if (data)
- XFree(data);
- if (_glfw.x11.xdnd.version >= 2)
- {
- XEvent reply = { ClientMessage };
- reply.xclient.window = _glfw.x11.xdnd.source;
- reply.xclient.message_type = _glfw.x11.XdndFinished;
- reply.xclient.format = 32;
- reply.xclient.data.l[0] = window->x11.handle;
- reply.xclient.data.l[1] = result;
- reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy;
- XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source,
- False, NoEventMask, &reply);
- XFlush(_glfw.x11.display);
- }
- }
- return;
- }
- case FocusIn:
- {
- if (event->xfocus.mode == NotifyGrab ||
- event->xfocus.mode == NotifyUngrab)
- {
- // Ignore focus events from popup indicator windows, window menu
- // key chords and window dragging
- return;
- }
- if (window->cursorMode == GLFW_CURSOR_DISABLED)
- disableCursor(window);
- _glfwInputWindowFocus(window, true);
- return;
- }
- case FocusOut:
- {
- if (event->xfocus.mode == NotifyGrab ||
- event->xfocus.mode == NotifyUngrab)
- {
- // Ignore focus events from popup indicator windows, window menu
- // key chords and window dragging
- return;
- }
- if (window->cursorMode == GLFW_CURSOR_DISABLED)
- enableCursor(window);
- if (window->monitor && window->autoIconify)
- _glfwPlatformIconifyWindow(window);
- _glfwInputWindowFocus(window, false);
- return;
- }
- case Expose:
- {
- _glfwInputWindowDamage(window);
- return;
- }
- case PropertyNotify:
- {
- if (event->xproperty.state != PropertyNewValue)
- return;
- if (event->xproperty.atom == _glfw.x11.WM_STATE)
- {
- const int state = getWindowState(window);
- if (state != IconicState && state != NormalState)
- return;
- const bool iconified = (state == IconicState);
- if (window->x11.iconified != iconified)
- {
- if (window->monitor)
- {
- if (iconified)
- releaseMonitor(window);
- else
- acquireMonitor(window);
- }
- window->x11.iconified = iconified;
- _glfwInputWindowIconify(window, iconified);
- }
- }
- else if (event->xproperty.atom == _glfw.x11.NET_WM_STATE)
- {
- const bool maximized = _glfwPlatformWindowMaximized(window);
- if (window->x11.maximized != maximized)
- {
- window->x11.maximized = maximized;
- int width, height;
- _glfwPlatformGetWindowSize(window, &width, &height);
- updateNormalHints(window, width, height);
- _glfwInputWindowMaximize(window, maximized);
- }
- }
- return;
- }
- case DestroyNotify:
- return;
- }
- #undef UPDATE_KEYMAP_IF_NEEDED
- }
- //////////////////////////////////////////////////////////////////////////
- ////// GLFW internal API //////
- //////////////////////////////////////////////////////////////////////////
- // Retrieve a single window property of the specified type
- // Inspired by fghGetWindowProperty from freeglut
- //
- unsigned long _glfwGetWindowPropertyX11(Window window,
- Atom property,
- Atom type,
- unsigned char** value)
- {
- Atom actualType;
- int actualFormat;
- unsigned long itemCount, bytesAfter;
- XGetWindowProperty(_glfw.x11.display,
- window,
- property,
- 0,
- LONG_MAX,
- False,
- type,
- &actualType,
- &actualFormat,
- &itemCount,
- &bytesAfter,
- value);
- return itemCount;
- }
- bool _glfwIsVisualTransparentX11(Visual* visual)
- {
- if (!_glfw.x11.xrender.available)
- return false;
- XRenderPictFormat* pf = XRenderFindVisualFormat(_glfw.x11.display, visual);
- return pf && pf->direct.alphaMask;
- }
- // Push contents of our selection to clipboard manager
- //
- void _glfwPushSelectionToManagerX11(void)
- {
- XConvertSelection(_glfw.x11.display,
- _glfw.x11.CLIPBOARD_MANAGER,
- _glfw.x11.SAVE_TARGETS,
- None,
- _glfw.x11.helperWindowHandle,
- CurrentTime);
- for (;;)
- {
- XEvent event;
- while (XCheckIfEvent(_glfw.x11.display, &event, isSelectionEvent, NULL))
- {
- switch (event.type)
- {
- case SelectionRequest:
- handleSelectionRequest(&event);
- break;
- case SelectionClear:
- handleSelectionClear(&event);
- break;
- case SelectionNotify:
- {
- if (event.xselection.target == _glfw.x11.SAVE_TARGETS)
- {
- // This means one of two things; either the selection
- // was not owned, which means there is no clipboard
- // manager, or the transfer to the clipboard manager has
- // completed
- // In either case, it means we are done here
- return;
- }
- break;
- }
- }
- }
- waitForX11Event(-1);
- }
- }
- //////////////////////////////////////////////////////////////////////////
- ////// GLFW platform API //////
- //////////////////////////////////////////////////////////////////////////
- int _glfwPlatformCreateWindow(_GLFWwindow* window,
- const _GLFWwndconfig* wndconfig,
- const _GLFWctxconfig* ctxconfig,
- const _GLFWfbconfig* fbconfig)
- {
- Visual* visual = NULL;
- int depth;
- if (ctxconfig->client != GLFW_NO_API)
- {
- if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
- {
- if (!_glfwInitGLX())
- return false;
- if (!_glfwChooseVisualGLX(wndconfig, ctxconfig, fbconfig, &visual, &depth))
- return false;
- }
- else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
- {
- if (!_glfwInitEGL())
- return false;
- if (!_glfwChooseVisualEGL(wndconfig, ctxconfig, fbconfig, &visual, &depth))
- return false;
- }
- else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
- {
- if (!_glfwInitOSMesa())
- return false;
- }
- }
- if (!visual)
- {
- visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen);
- depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen);
- }
- if (!createNativeWindow(window, wndconfig, visual, depth))
- return false;
- if (ctxconfig->client != GLFW_NO_API)
- {
- if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API)
- {
- if (!_glfwCreateContextGLX(window, ctxconfig, fbconfig))
- return false;
- }
- else if (ctxconfig->source == GLFW_EGL_CONTEXT_API)
- {
- if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
- return false;
- }
- else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API)
- {
- if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig))
- return false;
- }
- }
- if (window->monitor)
- {
- _glfwPlatformShowWindow(window);
- updateWindowMode(window);
- acquireMonitor(window);
- }
- XFlush(_glfw.x11.display);
- return true;
- }
- void _glfwPlatformDestroyWindow(_GLFWwindow* window)
- {
- if (_glfw.x11.disabledCursorWindow == window)
- _glfw.x11.disabledCursorWindow = NULL;
- if (window->monitor)
- releaseMonitor(window);
- if (window->context.destroy)
- window->context.destroy(window);
- if (window->x11.handle)
- {
- XDeleteContext(_glfw.x11.display, window->x11.handle, _glfw.x11.context);
- XUnmapWindow(_glfw.x11.display, window->x11.handle);
- XDestroyWindow(_glfw.x11.display, window->x11.handle);
- window->x11.handle = (Window) 0;
- }
- if (window->x11.colormap)
- {
- XFreeColormap(_glfw.x11.display, window->x11.colormap);
- window->x11.colormap = (Colormap) 0;
- }
- XFlush(_glfw.x11.display);
- }
- void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
- {
- #if defined(X_HAVE_UTF8_STRING)
- Xutf8SetWMProperties(_glfw.x11.display,
- window->x11.handle,
- title, title,
- NULL, 0,
- NULL, NULL, NULL);
- #else
- // This may be a slightly better fallback than using XStoreName and
- // XSetIconName, which always store their arguments using STRING
- XmbSetWMProperties(_glfw.x11.display,
- window->x11.handle,
- title, title,
- NULL, 0,
- NULL, NULL, NULL);
- #endif
- XChangeProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.NET_WM_NAME, _glfw.x11.UTF8_STRING, 8,
- PropModeReplace,
- (unsigned char*) title, strlen(title));
- XChangeProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.NET_WM_ICON_NAME, _glfw.x11.UTF8_STRING, 8,
- PropModeReplace,
- (unsigned char*) title, strlen(title));
- 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;
- unsigned long* icon = calloc(longCount, sizeof(unsigned long));
- unsigned 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; j++)
- {
- unsigned char *p = images->pixels + j * 4;
- const unsigned char r = *p++, g = *p++, b = *p++, a = *p++;
- *target++ = a << 24 | (r << 16) | (g << 8) | b;
- }
- }
- 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)
- {
- Window dummy;
- int x = 0, y = 0;
- _glfwGrabErrorHandlerX11();
- XTranslateCoordinates(_glfw.x11.display, window->x11.handle, _glfw.x11.root,
- 0, 0, &x, &y, &dummy);
- _glfwReleaseErrorHandlerX11();
- if (_glfw.x11.errorCode != Success)
- _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to get window position");
- if (xpos)
- *xpos = x;
- if (ypos)
- *ypos = y;
- }
- void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos)
- {
- // HACK: Explicitly setting PPosition to any value causes some WMs, notably
- // Compiz and Metacity, to honor the position of unmapped windows
- if (!_glfwPlatformWindowVisible(window))
- {
- long supplied;
- XSizeHints* hints = XAllocSizeHints();
- if (XGetWMNormalHints(_glfw.x11.display, window->x11.handle, hints, &supplied))
- {
- hints->flags |= PPosition;
- hints->x = hints->y = 0;
- XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints);
- }
- XFree(hints);
- }
- XMoveWindow(_glfw.x11.display, window->x11.handle, xpos, ypos);
- XFlush(_glfw.x11.display);
- }
- void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
- {
- XWindowAttributes attribs;
- XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &attribs);
- if (width)
- *width = attribs.width;
- if (height)
- *height = attribs.height;
- }
- void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
- {
- if (window->monitor)
- {
- if (window->monitor->window == window)
- acquireMonitor(window);
- }
- else
- {
- if (!window->resizable)
- updateNormalHints(window, width, height);
- XResizeWindow(_glfw.x11.display, window->x11.handle, width, height);
- }
- XFlush(_glfw.x11.display);
- }
- void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
- int minwidth UNUSED, int minheight UNUSED,
- int maxwidth UNUSED, int maxheight UNUSED)
- {
- int width, height;
- _glfwPlatformGetWindowSize(window, &width, &height);
- updateNormalHints(window, width, height);
- XFlush(_glfw.x11.display);
- }
- void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer UNUSED, int denom UNUSED)
- {
- int width, height;
- _glfwPlatformGetWindowSize(window, &width, &height);
- updateNormalHints(window, width, height);
- XFlush(_glfw.x11.display);
- }
- void _glfwPlatformSetWindowSizeIncrements(_GLFWwindow* window, int widthincr UNUSED, int heightincr UNUSED)
- {
- int width, height;
- _glfwPlatformGetWindowSize(window, &width, &height);
- updateNormalHints(window, width, height);
- XFlush(_glfw.x11.display);
- }
- void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height)
- {
- _glfwPlatformGetWindowSize(window, width, height);
- }
- void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
- int* left, int* top,
- int* right, int* bottom)
- {
- long* extents = NULL;
- if (window->monitor || !window->decorated)
- return;
- if (_glfw.x11.NET_FRAME_EXTENTS == None)
- return;
- if (!_glfwPlatformWindowVisible(window) &&
- _glfw.x11.NET_REQUEST_FRAME_EXTENTS)
- {
- XEvent event;
- // Ensure _NET_FRAME_EXTENTS is set, allowing glfwGetWindowFrameSize to
- // function before the window is mapped
- sendEventToWM(window, _glfw.x11.NET_REQUEST_FRAME_EXTENTS,
- 0, 0, 0, 0, 0);
- // HACK: Use a timeout because earlier versions of some window managers
- // (at least Unity, Fluxbox and Xfwm) failed to send the reply
- // They have been fixed but broken versions are still in the wild
- // If you are affected by this and your window manager is NOT
- // listed above, PLEASE report it to their and our issue trackers
- while (!XCheckIfEvent(_glfw.x11.display,
- &event,
- isFrameExtentsEvent,
- (XPointer) window))
- {
- if (!waitForX11Event(ms_to_monotonic_t(500ll)))
- {
- _glfwInputError(GLFW_PLATFORM_ERROR,
- "X11: The window manager has a broken _NET_REQUEST_FRAME_EXTENTS implementation; please report this issue");
- return;
- }
- }
- }
- if (_glfwGetWindowPropertyX11(window->x11.handle,
- _glfw.x11.NET_FRAME_EXTENTS,
- XA_CARDINAL,
- (unsigned char**) &extents) == 4)
- {
- if (left)
- *left = extents[0];
- if (top)
- *top = extents[2];
- if (right)
- *right = extents[1];
- if (bottom)
- *bottom = extents[3];
- }
- if (extents)
- XFree(extents);
- }
- void _glfwPlatformGetWindowContentScale(_GLFWwindow* window UNUSED,
- float* xscale, float* yscale)
- {
- if (xscale)
- *xscale = _glfw.x11.contentScaleX;
- if (yscale)
- *yscale = _glfw.x11.contentScaleY;
- }
- monotonic_t _glfwPlatformGetDoubleClickInterval(_GLFWwindow* window UNUSED)
- {
- return ms_to_monotonic_t(500ll);
- }
- void _glfwPlatformIconifyWindow(_GLFWwindow* window)
- {
- XIconifyWindow(_glfw.x11.display, window->x11.handle, _glfw.x11.screen);
- XFlush(_glfw.x11.display);
- }
- void _glfwPlatformRestoreWindow(_GLFWwindow* window)
- {
- if (_glfwPlatformWindowIconified(window))
- {
- XMapWindow(_glfw.x11.display, window->x11.handle);
- waitForVisibilityNotify(window);
- }
- else if (_glfwPlatformWindowVisible(window))
- {
- if (_glfw.x11.NET_WM_STATE &&
- _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT &&
- _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
- {
- sendEventToWM(window,
- _glfw.x11.NET_WM_STATE,
- _NET_WM_STATE_REMOVE,
- _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
- _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
- 1, 0);
- }
- }
- XFlush(_glfw.x11.display);
- }
- void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
- {
- if (!_glfw.x11.NET_WM_STATE ||
- !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
- !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
- {
- return;
- }
- if (_glfwPlatformWindowVisible(window))
- {
- sendEventToWM(window,
- _glfw.x11.NET_WM_STATE,
- _NET_WM_STATE_ADD,
- _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
- _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ,
- 1, 0);
- }
- else
- {
- Atom* states = NULL;
- unsigned long count =
- _glfwGetWindowPropertyX11(window->x11.handle,
- _glfw.x11.NET_WM_STATE,
- XA_ATOM,
- (unsigned char**) &states);
- // NOTE: We don't check for failure as this property may not exist yet
- // and that's fine (and we'll create it implicitly with append)
- Atom missing[2] =
- {
- _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT,
- _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ
- };
- unsigned long missingCount = 2;
- for (unsigned long i = 0; i < count; i++)
- {
- for (unsigned long j = 0; j < missingCount; j++)
- {
- if (states[i] == missing[j])
- {
- missing[j] = missing[missingCount - 1];
- missingCount--;
- }
- }
- }
- if (states)
- XFree(states);
- if (!missingCount)
- return;
- XChangeProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
- PropModeAppend,
- (unsigned char*) missing,
- missingCount);
- }
- XFlush(_glfw.x11.display);
- }
- void _glfwPlatformShowWindow(_GLFWwindow* window)
- {
- if (_glfwPlatformWindowVisible(window))
- return;
- XMapWindow(_glfw.x11.display, window->x11.handle);
- waitForVisibilityNotify(window);
- }
- void _glfwPlatformHideWindow(_GLFWwindow* window)
- {
- XUnmapWindow(_glfw.x11.display, window->x11.handle);
- XFlush(_glfw.x11.display);
- }
- void _glfwPlatformRequestWindowAttention(_GLFWwindow* window)
- {
- if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION)
- return;
- sendEventToWM(window,
- _glfw.x11.NET_WM_STATE,
- _NET_WM_STATE_ADD,
- _glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION,
- 0, 1, 0);
- }
- int _glfwPlatformWindowBell(_GLFWwindow* window)
- {
- return XkbBell(_glfw.x11.display, window->x11.handle, 100, (Atom)0) ? true : false;
- }
- void _glfwPlatformFocusWindow(_GLFWwindow* window)
- {
- if (_glfw.x11.NET_ACTIVE_WINDOW)
- sendEventToWM(window, _glfw.x11.NET_ACTIVE_WINDOW, 1, 0, 0, 0, 0);
- else if (_glfwPlatformWindowVisible(window))
- {
- XRaiseWindow(_glfw.x11.display, window->x11.handle);
- XSetInputFocus(_glfw.x11.display, window->x11.handle,
- RevertToParent, CurrentTime);
- }
- XFlush(_glfw.x11.display);
- }
- void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
- _GLFWmonitor* monitor,
- int xpos, int ypos,
- int width, int height,
- int refreshRate UNUSED)
- {
- if (window->monitor == monitor)
- {
- if (monitor)
- {
- if (monitor->window == window)
- acquireMonitor(window);
- }
- else
- {
- if (!window->resizable)
- updateNormalHints(window, width, height);
- XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
- xpos, ypos, width, height);
- }
- XFlush(_glfw.x11.display);
- return;
- }
- if (window->monitor)
- releaseMonitor(window);
- _glfwInputWindowMonitor(window, monitor);
- updateNormalHints(window, width, height);
- if (window->monitor)
- {
- if (!_glfwPlatformWindowVisible(window))
- {
- XMapRaised(_glfw.x11.display, window->x11.handle);
- waitForVisibilityNotify(window);
- }
- updateWindowMode(window);
- acquireMonitor(window);
- }
- else
- {
- updateWindowMode(window);
- XMoveResizeWindow(_glfw.x11.display, window->x11.handle,
- xpos, ypos, width, height);
- }
- XFlush(_glfw.x11.display);
- }
- int _glfwPlatformWindowFocused(_GLFWwindow* window)
- {
- Window focused;
- int state;
- XGetInputFocus(_glfw.x11.display, &focused, &state);
- return window->x11.handle == focused;
- }
- int _glfwPlatformWindowOccluded(_GLFWwindow* window UNUSED)
- {
- return false;
- }
- int _glfwPlatformWindowIconified(_GLFWwindow* window)
- {
- return getWindowState(window) == IconicState;
- }
- int _glfwPlatformWindowVisible(_GLFWwindow* window)
- {
- XWindowAttributes wa;
- XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &wa);
- return wa.map_state == IsViewable;
- }
- int _glfwPlatformWindowMaximized(_GLFWwindow* window)
- {
- Atom* states;
- unsigned long i;
- bool maximized = false;
- if (!_glfw.x11.NET_WM_STATE ||
- !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
- !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
- {
- return maximized;
- }
- const unsigned long count =
- _glfwGetWindowPropertyX11(window->x11.handle,
- _glfw.x11.NET_WM_STATE,
- XA_ATOM,
- (unsigned char**) &states);
- for (i = 0; i < count; i++)
- {
- if (states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT ||
- states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ)
- {
- maximized = true;
- break;
- }
- }
- if (states)
- XFree(states);
- return maximized;
- }
- int _glfwPlatformWindowHovered(_GLFWwindow* window)
- {
- Window w = _glfw.x11.root;
- while (w)
- {
- Window root;
- int rootX, rootY, childX, childY;
- unsigned int mask;
- _glfwGrabErrorHandlerX11();
- const Bool result = XQueryPointer(_glfw.x11.display, w,
- &root, &w, &rootX, &rootY,
- &childX, &childY, &mask);
- _glfwReleaseErrorHandlerX11();
- if (_glfw.x11.errorCode == BadWindow)
- w = _glfw.x11.root;
- else if (!result)
- return false;
- else if (w == window->x11.handle)
- return true;
- }
- return false;
- }
- int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
- {
- if (!window->x11.transparent)
- return false;
- return XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx) != None;
- }
- void _glfwPlatformSetWindowResizable(_GLFWwindow* window, bool enabled UNUSED)
- {
- int width, height;
- _glfwPlatformGetWindowSize(window, &width, &height);
- updateNormalHints(window, width, height);
- }
- void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, bool enabled)
- {
- struct
- {
- unsigned long flags;
- unsigned long functions;
- unsigned long decorations;
- long input_mode;
- unsigned long status;
- } hints = {0};
- hints.flags = MWM_HINTS_DECORATIONS;
- hints.decorations = enabled ? MWM_DECOR_ALL : 0;
- XChangeProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.MOTIF_WM_HINTS,
- _glfw.x11.MOTIF_WM_HINTS, 32,
- PropModeReplace,
- (unsigned char*) &hints,
- sizeof(hints) / sizeof(long));
- }
- void _glfwPlatformSetWindowFloating(_GLFWwindow* window, bool enabled)
- {
- if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_ABOVE)
- return;
- if (_glfwPlatformWindowVisible(window))
- {
- const long action = enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
- sendEventToWM(window,
- _glfw.x11.NET_WM_STATE,
- action,
- _glfw.x11.NET_WM_STATE_ABOVE,
- 0, 1, 0);
- }
- else
- {
- Atom* states = NULL;
- unsigned long i, count;
- count = _glfwGetWindowPropertyX11(window->x11.handle,
- _glfw.x11.NET_WM_STATE,
- XA_ATOM,
- (unsigned char**) &states);
- // NOTE: We don't check for failure as this property may not exist yet
- // and that's fine (and we'll create it implicitly with append)
- if (enabled)
- {
- for (i = 0; i < count; i++)
- {
- if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE)
- break;
- }
- if (i < count)
- return;
- XChangeProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
- PropModeAppend,
- (unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE,
- 1);
- }
- else if (states)
- {
- for (i = 0; i < count; i++)
- {
- if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE)
- break;
- }
- if (i == count)
- return;
- states[i] = states[count - 1];
- count--;
- XChangeProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.NET_WM_STATE, XA_ATOM, 32,
- PropModeReplace, (unsigned char*) states, count);
- }
- if (states)
- XFree(states);
- }
- XFlush(_glfw.x11.display);
- }
- void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, bool enabled)
- {
- if (!_glfw.x11.xshape.available)
- return;
- if (enabled)
- {
- Region region = XCreateRegion();
- XShapeCombineRegion(_glfw.x11.display, window->x11.handle,
- ShapeInput, 0, 0, region, ShapeSet);
- XDestroyRegion(region);
- }
- else
- {
- XShapeCombineMask(_glfw.x11.display, window->x11.handle,
- ShapeInput, 0, 0, None, ShapeSet);
- }
- }
- float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
- {
- float opacity = 1.f;
- if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx))
- {
- CARD32* value = NULL;
- if (_glfwGetWindowPropertyX11(window->x11.handle,
- _glfw.x11.NET_WM_WINDOW_OPACITY,
- XA_CARDINAL,
- (unsigned char**) &value))
- {
- opacity = (float) (*value / (double) 0xffffffffu);
- }
- if (value)
- XFree(value);
- }
- return opacity;
- }
- void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
- {
- const CARD32 value = (CARD32) (0xffffffffu * (double) opacity);
- XChangeProperty(_glfw.x11.display, window->x11.handle,
- _glfw.x11.NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32,
- PropModeReplace, (unsigned char*) &value, 1);
- }
- static unsigned
- dispatch_x11_queued_events(int num_events) {
- unsigned dispatched = num_events > 0 ? num_events : 0;
- while (num_events-- > 0) {
- XEvent event;
- XNextEvent(_glfw.x11.display, &event);
- processEvent(&event);
- }
- return dispatched;
- }
- static unsigned
- _glfwDispatchX11Events(void) {
- _GLFWwindow* window;
- unsigned dispatched = 0;
- #if defined(__linux__)
- if (_glfw.joysticksInitialized)
- _glfwDetectJoystickConnectionLinux();
- #endif
- dispatched += dispatch_x11_queued_events(XEventsQueued(_glfw.x11.display, QueuedAfterFlush));
- window = _glfw.x11.disabledCursorWindow;
- if (window)
- {
- int width, height;
- _glfwPlatformGetWindowSize(window, &width, &height);
- // NOTE: Re-center the cursor only if it has moved since the last call,
- // to avoid breaking glfwWaitEvents with MotionNotify
- if (window->x11.lastCursorPosX != width / 2 ||
- window->x11.lastCursorPosY != height / 2)
- {
- _glfwPlatformSetCursorPos(window, width / 2.f, height / 2.f);
- }
- }
- XFlush(_glfw.x11.display);
- // XFlush can cause events to be queued, we don't use QueuedAfterFlush here
- // as something might have inserted events into the queue, but we want to guarantee
- // a flush.
- dispatched += dispatch_x11_queued_events(XEventsQueued(_glfw.x11.display, QueuedAlready));
- return dispatched;
- }
- void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, bool enabled)
- {
- if (!_glfw.x11.xi.available)
- return;
- if (_glfw.x11.disabledCursorWindow != window)
- return;
- if (enabled)
- enableRawMouseMotion(window);
- else
- disableRawMouseMotion(window);
- }
- bool _glfwPlatformRawMouseMotionSupported(void)
- {
- return _glfw.x11.xi.available;
- }
- void _glfwPlatformPollEvents(void)
- {
- _glfwDispatchX11Events();
- handleEvents(0);
- }
- void _glfwPlatformWaitEvents(void)
- {
- monotonic_t timeout = _glfwDispatchX11Events() ? 0 : -1;
- handleEvents(timeout);
- }
- void _glfwPlatformWaitEventsTimeout(monotonic_t timeout)
- {
- if (_glfwDispatchX11Events()) timeout = 0;
- handleEvents(timeout);
- }
- void _glfwPlatformPostEmptyEvent(void)
- {
- wakeupEventLoop(&_glfw.x11.eventLoopData);
- }
- void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
- {
- Window root, child;
- int rootX, rootY, childX, childY;
- unsigned int mask;
- XQueryPointer(_glfw.x11.display, window->x11.handle,
- &root, &child,
- &rootX, &rootY, &childX, &childY,
- &mask);
- if (xpos)
- *xpos = childX;
- if (ypos)
- *ypos = childY;
- }
- void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
- {
- // Store the new position so it can be recognized later
- window->x11.warpCursorPosX = (int) x;
- window->x11.warpCursorPosY = (int) y;
- XWarpPointer(_glfw.x11.display, None, window->x11.handle,
- 0,0,0,0, (int) x, (int) y);
- XFlush(_glfw.x11.display);
- }
- void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
- {
- if (mode == GLFW_CURSOR_DISABLED)
- {
- if (_glfwPlatformWindowFocused(window))
- disableCursor(window);
- }
- else if (_glfw.x11.disabledCursorWindow == window)
- enableCursor(window);
- else
- updateCursorImage(window);
- XFlush(_glfw.x11.display);
- }
- const char* _glfwPlatformGetNativeKeyName(int native_key)
- {
- return glfw_xkb_keysym_name(native_key);
- }
- int _glfwPlatformGetNativeKeyForKey(uint32_t key)
- {
- return glfw_xkb_sym_for_key(key);
- }
- int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
- const GLFWimage* image,
- int xhot, int yhot, int count UNUSED)
- {
- cursor->x11.handle = _glfwCreateCursorX11(image, xhot, yhot);
- if (!cursor->x11.handle)
- return false;
- return true;
- }
- static int
- set_cursor_from_font(_GLFWcursor* cursor, int native) {
- cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, native);
- if (!cursor->x11.handle) {
- _glfwInputError(GLFW_PLATFORM_ERROR,
- "X11: Failed to create standard cursor");
- return false;
- }
- return true;
- }
- static bool
- try_cursor_names(_GLFWcursor *cursor, int arg_count, ...) {
- va_list ap;
- va_start(ap, arg_count);
- const char *first_name = "";
- for (int i = 0; i < arg_count; i++) {
- const char *name = va_arg(ap, const char *);
- first_name = name;
- cursor->x11.handle = XcursorLibraryLoadCursor(_glfw.x11.display, name);
- if (cursor->x11.handle) break;
- }
- va_end(ap);
- if (!cursor->x11.handle) {
- _glfwInputError(GLFW_PLATFORM_ERROR,
- "X11: Failed to load standard cursor: %s with %d aliases via Xcursor library", first_name, arg_count);
- return false;
- }
- return true;
- }
- int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, GLFWCursorShape shape)
- {
- switch(shape) {
- /* start glfw to xc mapping (auto generated by gen-key-constants.py do not edit) */
- case GLFW_DEFAULT_CURSOR: return set_cursor_from_font(cursor, XC_left_ptr);
- case GLFW_TEXT_CURSOR: return set_cursor_from_font(cursor, XC_xterm);
- case GLFW_POINTER_CURSOR: return set_cursor_from_font(cursor, XC_hand2);
- case GLFW_HELP_CURSOR: return set_cursor_from_font(cursor, XC_question_arrow);
- case GLFW_WAIT_CURSOR: return set_cursor_from_font(cursor, XC_clock);
- case GLFW_PROGRESS_CURSOR: return try_cursor_names(cursor, 3, "progress", "half-busy", "left_ptr_watch");
- case GLFW_CROSSHAIR_CURSOR: return set_cursor_from_font(cursor, XC_tcross);
- case GLFW_CELL_CURSOR: return set_cursor_from_font(cursor, XC_plus);
- case GLFW_VERTICAL_TEXT_CURSOR: return try_cursor_names(cursor, 1, "vertical-text");
- case GLFW_MOVE_CURSOR: return set_cursor_from_font(cursor, XC_fleur);
- case GLFW_E_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_right_side);
- case GLFW_NE_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_top_right_corner);
- case GLFW_NW_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_top_left_corner);
- case GLFW_N_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_top_side);
- case GLFW_SE_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_bottom_right_corner);
- case GLFW_SW_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_bottom_left_corner);
- case GLFW_S_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_bottom_side);
- case GLFW_W_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_left_side);
- case GLFW_EW_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_sb_h_double_arrow);
- case GLFW_NS_RESIZE_CURSOR: return set_cursor_from_font(cursor, XC_sb_v_double_arrow);
- case GLFW_NESW_RESIZE_CURSOR: return try_cursor_names(cursor, 3, "nesw-resize", "size_bdiag", "size-bdiag");
- case GLFW_NWSE_RESIZE_CURSOR: return try_cursor_names(cursor, 3, "nwse-resize", "size_fdiag", "size-fdiag");
- case GLFW_ZOOM_IN_CURSOR: return try_cursor_names(cursor, 2, "zoom-in", "zoom_in");
- case GLFW_ZOOM_OUT_CURSOR: return try_cursor_names(cursor, 2, "zoom-out", "zoom_out");
- case GLFW_ALIAS_CURSOR: return try_cursor_names(cursor, 1, "dnd-link");
- case GLFW_COPY_CURSOR: return try_cursor_names(cursor, 1, "dnd-copy");
- case GLFW_NOT_ALLOWED_CURSOR: return try_cursor_names(cursor, 3, "not-allowed", "forbidden", "crossed_circle");
- case GLFW_NO_DROP_CURSOR: return try_cursor_names(cursor, 2, "no-drop", "dnd-no-drop");
- case GLFW_GRAB_CURSOR: return set_cursor_from_font(cursor, XC_hand1);
- case GLFW_GRABBING_CURSOR: return try_cursor_names(cursor, 3, "grabbing", "closedhand", "dnd-none");
- /* end glfw to xc mapping */
- case GLFW_INVALID_CURSOR: return false;
- }
- return false;
- }
- void _glfwPlatformDestroyCursor(_GLFWcursor* cursor)
- {
- if (cursor->x11.handle)
- XFreeCursor(_glfw.x11.display, cursor->x11.handle);
- }
- void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor UNUSED)
- {
- if (window->cursorMode == GLFW_CURSOR_NORMAL)
- {
- updateCursorImage(window);
- XFlush(_glfw.x11.display);
- }
- }
- static MimeAtom atom_for_mime(const char *mime) {
- for (size_t i = 0; i < _glfw.x11.mime_atoms.sz; i++) {
- MimeAtom ma = _glfw.x11.mime_atoms.array[i];
- if (strcmp(ma.mime, mime) == 0) {
- return ma;
- }
- }
- MimeAtom ma = {.mime=_glfw_strdup(mime), .atom=XInternAtom(_glfw.x11.display, mime, 0)};
- if (_glfw.x11.mime_atoms.capacity < _glfw.x11.mime_atoms.sz + 1) {
- _glfw.x11.mime_atoms.capacity += 32;
- _glfw.x11.mime_atoms.array = realloc(_glfw.x11.mime_atoms.array, _glfw.x11.mime_atoms.capacity * sizeof(_glfw.x11.mime_atoms.array[0]));
- }
- _glfw.x11.mime_atoms.array[_glfw.x11.mime_atoms.sz++] = ma;
- return ma;
- }
- void _glfwPlatformSetClipboard(GLFWClipboardType t) {
- Atom which = None;
- _GLFWClipboardData *cd = NULL;
- AtomArray *aa = NULL;
- switch (t) {
- case GLFW_CLIPBOARD: which = _glfw.x11.CLIPBOARD; cd = &_glfw.clipboard; aa = &_glfw.x11.clipboard_atoms; break;
- case GLFW_PRIMARY_SELECTION: which = _glfw.x11.PRIMARY; cd = &_glfw.primary; aa = &_glfw.x11.primary_atoms; break;
- }
- XSetSelectionOwner(_glfw.x11.display, which, _glfw.x11.helperWindowHandle, CurrentTime);
- if (XGetSelectionOwner(_glfw.x11.display, which) != _glfw.x11.helperWindowHandle) {
- _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to become owner of clipboard selection");
- }
- if (aa->capacity < cd->num_mime_types + 32) {
- aa->capacity = cd->num_mime_types + 32;
- aa->array = reallocarray(aa->array, aa->capacity, sizeof(aa->array[0]));
- }
- aa->sz = 0;
- for (size_t i = 0; i < cd->num_mime_types; i++) {
- MimeAtom *a = aa->array + aa->sz++;
- *a = atom_for_mime(cd->mime_types[i]);
- if (strcmp(cd->mime_types[i], "text/plain") == 0) {
- a = aa->array + aa->sz++;
- a->atom = _glfw.x11.UTF8_STRING;
- a->mime = "text/plain";
- }
- }
- }
- typedef struct chunked_writer {
- char *buf; size_t sz, cap;
- bool is_self_offer;
- } chunked_writer;
- static bool
- write_chunk(void *object, const char *data, size_t sz) {
- chunked_writer *cw = object;
- if (data) {
- if (cw->cap < cw->sz + sz) {
- cw->cap = MAX(cw->cap * 2, cw->sz + 8*sz);
- cw->buf = realloc(cw->buf, cw->cap * sizeof(cw->buf[0]));
- }
- memcpy(cw->buf + cw->sz, data, sz);
- cw->sz += sz;
- } else if (sz == 1) cw->is_self_offer = true;
- return true;
- }
- static void
- get_available_mime_types(Atom which_clipboard, GLFWclipboardwritedatafun write_data, void *object) {
- chunked_writer cw = {0};
- getSelectionString(which_clipboard, &_glfw.x11.TARGETS, 1, write_chunk, &cw, false);
- if (cw.is_self_offer) {
- write_data(object, NULL, 1);
- return;
- }
- size_t count = 0;
- bool ok = true;
- if (cw.buf) {
- Atom *atoms = (Atom*)cw.buf;
- count = cw.sz / sizeof(Atom);
- char **names = calloc(count, sizeof(char*));
- get_atom_names(atoms, count, names);
- for (size_t i = 0; i < count; i++) {
- if (strchr(names[i], '/')) {
- if (ok) ok = write_data(object, names[i], strlen(names[i]));
- } else {
- if (atoms[i] == _glfw.x11.UTF8_STRING || atoms[i] == XA_STRING) {
- if (ok) ok = write_data(object, "text/plain", strlen("text/plain"));
- }
- }
- XFree(names[i]);
- }
- free(cw.buf);
- free(names);
- }
- }
- void
- _glfwPlatformGetClipboard(GLFWClipboardType clipboard_type, const char* mime_type, GLFWclipboardwritedatafun write_data, void *object) {
- Atom atoms[4], which = clipboard_type == GLFW_PRIMARY_SELECTION ? _glfw.x11.PRIMARY : _glfw.x11.CLIPBOARD;
- if (mime_type == NULL) {
- get_available_mime_types(which, write_data, object);
- return;
- }
- size_t count = 0;
- if (strcmp(mime_type, "text/plain") == 0) {
- // UTF8_STRING is what xclip uses by default, and there are people out there that expect to be able to paste from it with a single read operation. See https://github.com/kovidgoyal/kitty/issues/5842
- // Also ancient versions of GNOME use DOS line endings even for text/plain;charset=utf-8. See https://github.com/kovidgoyal/kitty/issues/5528#issuecomment-1325348218
- atoms[count++] = _glfw.x11.UTF8_STRING;
- // we need to do this because GTK/GNOME is moronic they convert text/plain to DOS line endings, see
- // https://gitlab.gnome.org/GNOME/gtk/-/issues/2307
- atoms[count++] = atom_for_mime("text/plain;charset=utf-8").atom;
- atoms[count++] = atom_for_mime("text/plain").atom;
- atoms[count++] = XA_STRING;
- } else {
- atoms[count++] = atom_for_mime(mime_type).atom;
- }
- getSelectionString(which, atoms, count, write_data, object, true);
- }
- EGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs)
- {
- if (_glfw.egl.ANGLE_platform_angle)
- {
- int type = 0;
- if (_glfw.egl.ANGLE_platform_angle_opengl)
- {
- if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_OPENGL)
- type = EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE;
- }
- if (_glfw.egl.ANGLE_platform_angle_vulkan)
- {
- if (_glfw.hints.init.angleType == GLFW_ANGLE_PLATFORM_TYPE_VULKAN)
- type = EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE;
- }
- if (type)
- {
- *attribs = calloc(5, sizeof(EGLint));
- (*attribs)[0] = EGL_PLATFORM_ANGLE_TYPE_ANGLE;
- (*attribs)[1] = type;
- (*attribs)[2] = EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE;
- (*attribs)[3] = EGL_PLATFORM_X11_EXT;
- (*attribs)[4] = EGL_NONE;
- return EGL_PLATFORM_ANGLE_ANGLE;
- }
- }
- if (_glfw.egl.EXT_platform_base && _glfw.egl.EXT_platform_x11)
- return EGL_PLATFORM_X11_EXT;
- return 0;
- }
- EGLNativeDisplayType _glfwPlatformGetEGLNativeDisplay(void)
- {
- return _glfw.x11.display;
- }
- EGLNativeWindowType _glfwPlatformGetEGLNativeWindow(_GLFWwindow* window)
- {
- if (_glfw.egl.platform)
- return &window->x11.handle;
- else
- return (EGLNativeWindowType) window->x11.handle;
- }
- void _glfwPlatformGetRequiredInstanceExtensions(char** extensions)
- {
- if (!_glfw.vk.KHR_surface)
- return;
- if (!_glfw.vk.KHR_xcb_surface)
- {
- if (!_glfw.vk.KHR_xlib_surface)
- return;
- }
- extensions[0] = "VK_KHR_surface";
- // NOTE: VK_KHR_xcb_surface is preferred due to some early ICDs exposing but
- // not correctly implementing VK_KHR_xlib_surface
- if (_glfw.vk.KHR_xcb_surface)
- extensions[1] = "VK_KHR_xcb_surface";
- else
- extensions[1] = "VK_KHR_xlib_surface";
- }
- int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
- VkPhysicalDevice device,
- uint32_t queuefamily)
- {
- VisualID visualID = XVisualIDFromVisual(DefaultVisual(_glfw.x11.display,
- _glfw.x11.screen));
- if (_glfw.vk.KHR_xcb_surface)
- {
- PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR
- vkGetPhysicalDeviceXcbPresentationSupportKHR =
- (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)
- vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR");
- if (!vkGetPhysicalDeviceXcbPresentationSupportKHR)
- {
- _glfwInputError(GLFW_API_UNAVAILABLE,
- "X11: Vulkan instance missing VK_KHR_xcb_surface extension");
- return false;
- }
- xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display);
- if (!connection)
- {
- _glfwInputError(GLFW_PLATFORM_ERROR,
- "X11: Failed to retrieve XCB connection");
- return false;
- }
- return vkGetPhysicalDeviceXcbPresentationSupportKHR(device,
- queuefamily,
- connection,
- visualID);
- }
- else
- {
- PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR
- vkGetPhysicalDeviceXlibPresentationSupportKHR =
- (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)
- vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR");
- if (!vkGetPhysicalDeviceXlibPresentationSupportKHR)
- {
- _glfwInputError(GLFW_API_UNAVAILABLE,
- "X11: Vulkan instance missing VK_KHR_xlib_surface extension");
- return false;
- }
- return vkGetPhysicalDeviceXlibPresentationSupportKHR(device,
- queuefamily,
- _glfw.x11.display,
- visualID);
- }
- }
- VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
- _GLFWwindow* window,
- const VkAllocationCallbacks* allocator,
- VkSurfaceKHR* surface)
- {
- if (_glfw.vk.KHR_xcb_surface)
- {
- VkResult err;
- VkXcbSurfaceCreateInfoKHR sci;
- PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR;
- xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display);
- if (!connection)
- {
- _glfwInputError(GLFW_PLATFORM_ERROR,
- "X11: Failed to retrieve XCB connection");
- return VK_ERROR_EXTENSION_NOT_PRESENT;
- }
- vkCreateXcbSurfaceKHR = (PFN_vkCreateXcbSurfaceKHR)
- vkGetInstanceProcAddr(instance, "vkCreateXcbSurfaceKHR");
- if (!vkCreateXcbSurfaceKHR)
- {
- _glfwInputError(GLFW_API_UNAVAILABLE,
- "X11: Vulkan instance missing VK_KHR_xcb_surface extension");
- return VK_ERROR_EXTENSION_NOT_PRESENT;
- }
- memset(&sci, 0, sizeof(sci));
- sci.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
- sci.connection = connection;
- sci.window = window->x11.handle;
- err = vkCreateXcbSurfaceKHR(instance, &sci, allocator, surface);
- if (err)
- {
- _glfwInputError(GLFW_PLATFORM_ERROR,
- "X11: Failed to create Vulkan XCB surface: %s",
- _glfwGetVulkanResultString(err));
- }
- return err;
- }
- else
- {
- VkResult err;
- VkXlibSurfaceCreateInfoKHR sci;
- PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR;
- vkCreateXlibSurfaceKHR = (PFN_vkCreateXlibSurfaceKHR)
- vkGetInstanceProcAddr(instance, "vkCreateXlibSurfaceKHR");
- if (!vkCreateXlibSurfaceKHR)
- {
- _glfwInputError(GLFW_API_UNAVAILABLE,
- "X11: Vulkan instance missing VK_KHR_xlib_surface extension");
- return VK_ERROR_EXTENSION_NOT_PRESENT;
- }
- memset(&sci, 0, sizeof(sci));
- sci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
- sci.dpy = _glfw.x11.display;
- sci.window = window->x11.handle;
- err = vkCreateXlibSurfaceKHR(instance, &sci, allocator, surface);
- if (err)
- {
- _glfwInputError(GLFW_PLATFORM_ERROR,
- "X11: Failed to create Vulkan X11 surface: %s",
- _glfwGetVulkanResultString(err));
- }
- return err;
- }
- }
- void
- _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) {
- glfw_xkb_update_ime_state(w, &_glfw.x11.xkb, ev);
- }
- int
- _glfwPlatformSetWindowBlur(_GLFWwindow *window, int blur_radius) {
- if (_glfw.x11._KDE_NET_WM_BLUR_BEHIND_REGION == None) {
- _glfw.x11._KDE_NET_WM_BLUR_BEHIND_REGION = XInternAtom(_glfw.x11.display, "_KDE_NET_WM_BLUR_BEHIND_REGION", False);
- }
- if (_glfw.x11._KDE_NET_WM_BLUR_BEHIND_REGION != None) {
- uint32_t data = 0;
- if (blur_radius > 0) {
- XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11._KDE_NET_WM_BLUR_BEHIND_REGION,
- XA_CARDINAL, 32, PropModeReplace, (unsigned char*) &data, 1);
- } else {
- XDeleteProperty(_glfw.x11.display, window->x11.handle, _glfw.x11._KDE_NET_WM_BLUR_BEHIND_REGION);
- }
- return 1;
- }
- return 0;
- }
- //////////////////////////////////////////////////////////////////////////
- ////// GLFW native API //////
- //////////////////////////////////////////////////////////////////////////
- GLFWAPI Display* glfwGetX11Display(void)
- {
- _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
- return _glfw.x11.display;
- }
- GLFWAPI unsigned long glfwGetX11Window(GLFWwindow* handle)
- {
- _GLFWwindow* window = (_GLFWwindow*) handle;
- _GLFW_REQUIRE_INIT_OR_RETURN(None);
- return window->x11.handle;
- }
- GLFWAPI int glfwGetNativeKeyForName(const char* keyName, bool caseSensitive) {
- return glfw_xkb_keysym_from_name(keyName, caseSensitive);
- }
- GLFWAPI unsigned long long glfwDBusUserNotify(const GLFWDBUSNotificationData *n, GLFWDBusnotificationcreatedfun callback, void *data) {
- return glfw_dbus_send_user_notification(n, callback, data);
- }
- GLFWAPI void glfwDBusSetUserNotificationHandler(GLFWDBusnotificationactivatedfun handler) {
- glfw_dbus_set_user_notification_activated_handler(handler);
- }
- GLFWAPI int glfwSetX11LaunchCommand(GLFWwindow *handle, char **argv, int argc)
- {
- _GLFW_REQUIRE_INIT_OR_RETURN(0);
- _GLFWwindow* window = (_GLFWwindow*) handle;
- return XSetCommand(_glfw.x11.display, window->x11.handle, argv, argc);
- }
- GLFWAPI void glfwSetX11WindowAsDock(int32_t x11_window_id) {
- _GLFW_REQUIRE_INIT();
- Atom type = _glfw.x11.NET_WM_WINDOW_TYPE_DOCK;
- XChangeProperty(_glfw.x11.display, x11_window_id,
- _glfw.x11.NET_WM_WINDOW_TYPE, XA_ATOM, 32,
- PropModeReplace, (unsigned char*) &type, 1);
- }
- GLFWAPI void glfwSetX11WindowStrut(int32_t x11_window_id, uint32_t dimensions[12]) {
- _GLFW_REQUIRE_INIT();
- XChangeProperty(_glfw.x11.display, x11_window_id,
- _glfw.x11.NET_WM_STRUT_PARTIAL, XA_CARDINAL, 32,
- PropModeReplace, (unsigned char*) dimensions, 12);
- }
|