1616
1717namespace nbl ::ui::xcb
1818{
19- class XCBHandle ;
19+ class XCBHandle ;
2020
2121 enum MotifFlags: uint32_t {
22- MWM_HINTS_NONE = 0 ,
23- MWM_HINTS_FUNCTIONS = (1L << 0 ),
24- MWM_HINTS_DECORATIONS = (1L << 1 ),
25- MWM_HINTS_INPUT_MODE = (1L << 2 ),
26- MWM_HINTS_STATUS = (1L << 3 ),
27- };
28-
29- enum MotifFunctions: uint32_t {
30- MWM_FUNC_NONE = 0 ,
31- MWM_FUNC_ALL = (1L << 0 ),
32- MWM_FUNC_RESIZE = (1L << 1 ),
33- MWM_FUNC_MOVE = (1L << 2 ),
34- MWM_FUNC_MINIMIZE = (1L << 3 ),
35- MWM_FUNC_MAXIMIZE = (1L << 4 ),
36- MWM_FUNC_CLOSE = (1L << 5 ),
37- };
38-
39- enum MotifDecorations: uint32_t {
40- MWM_DECOR_NONE = 0 ,
41- MWM_DECOR_ALL = (1L << 0 ),
42- MWM_DECOR_BORDER = (1L << 1 ),
43- MWM_DECOR_RESIZEH = (1L << 2 ),
44- MWM_DECOR_TITLE = (1L << 3 ),
45- MWM_DECOR_MENU = (1L << 4 ),
46- MWM_DECOR_MINIMIZE = (1L << 5 ),
47- MWM_DECOR_MAXIMIZE = (1L << 6 ),
48- };
22+ MWM_HINTS_NONE = 0 ,
23+ MWM_HINTS_FUNCTIONS = (1L << 0 ),
24+ MWM_HINTS_DECORATIONS = (1L << 1 ),
25+ MWM_HINTS_INPUT_MODE = (1L << 2 ),
26+ MWM_HINTS_STATUS = (1L << 3 ),
27+ };
28+
29+ enum MotifFunctions: uint32_t {
30+ MWM_FUNC_NONE = 0 ,
31+ MWM_FUNC_ALL = (1L << 0 ),
32+ MWM_FUNC_RESIZE = (1L << 1 ),
33+ MWM_FUNC_MOVE = (1L << 2 ),
34+ MWM_FUNC_MINIMIZE = (1L << 3 ),
35+ MWM_FUNC_MAXIMIZE = (1L << 4 ),
36+ MWM_FUNC_CLOSE = (1L << 5 ),
37+ };
38+
39+ enum MotifDecorations: uint32_t {
40+ MWM_DECOR_NONE = 0 ,
41+ MWM_DECOR_ALL = (1L << 0 ),
42+ MWM_DECOR_BORDER = (1L << 1 ),
43+ MWM_DECOR_RESIZEH = (1L << 2 ),
44+ MWM_DECOR_TITLE = (1L << 3 ),
45+ MWM_DECOR_MENU = (1L << 4 ),
46+ MWM_DECOR_MINIMIZE = (1L << 5 ),
47+ MWM_DECOR_MAXIMIZE = (1L << 6 ),
48+ };
4949
5050 // insane magic in xcb for window hinting good luck finding documentation
51- // https://fossies.org/linux/motif/lib/Xm/MwmUtil.h
52- struct MotifWmHints {
53- MotifFlags flags = MotifFlags::MWM_HINTS_NONE;
54- MotifFunctions functions = MotifFunctions::MWM_FUNC_NONE;
55- MotifDecorations decorations = MotifDecorations::MWM_DECOR_NONE;
56- uint32_t input_mode = 0 ; // unused
57- uint32_t status = 0 ; // unused
58- };
59-
60- inline MotifWmHints createFlagsToMotifWmHints (IWindow::E_CREATE_FLAGS flags) {
61- core::bitflag<MotifFlags> motifFlags (MWM_HINTS_NONE);
62- core::bitflag<MotifFunctions> motifFunctions (MWM_FUNC_NONE);
63- core::bitflag<MotifDecorations> motifDecorations (MWM_DECOR_NONE);
64- motifFlags |= MWM_HINTS_DECORATIONS;
65-
66- if (flags & IWindow::ECF_BORDERLESS) {
67- motifDecorations |= MWM_DECOR_ALL;
68- } else {
69- motifDecorations |= MWM_DECOR_BORDER;
70- motifDecorations |= MWM_DECOR_RESIZEH;
71- motifDecorations |= MWM_DECOR_TITLE;
72-
73- // minimize button
74- if (flags & IWindow::ECF_MINIMIZED) {
75- motifDecorations |= MWM_DECOR_MINIMIZE;
76- motifFunctions |= MWM_FUNC_MINIMIZE;
77- }
78-
79- // maximize button
80- if (flags & IWindow::ECF_MAXIMIZED) {
81- motifDecorations |= MWM_DECOR_MAXIMIZE;
82- motifFunctions |= MWM_FUNC_MAXIMIZE;
83- }
84-
85- // close button
86- motifFunctions |= MWM_FUNC_CLOSE;
87- }
88-
89- if (motifFunctions.value != MWM_FUNC_NONE) {
90- motifFlags |= MWM_HINTS_FUNCTIONS;
91- motifFunctions |= MWM_FUNC_RESIZE;
92- motifFunctions |= MWM_FUNC_MOVE;
93- } else {
94- motifFunctions = MWM_FUNC_ALL;
95- }
96-
97- MotifWmHints hints;
98- hints.flags = motifFlags.value ;
99- hints.functions = motifFunctions.value ;
100- hints.decorations = motifDecorations.value ;
101- hints.input_mode = 0 ;
102- hints.status = 0 ;
103- return hints;
104- }
105-
106- class XCBHandle final : public core::IReferenceCounted {
51+ // https://fossies.org/linux/motif/lib/Xm/MwmUtil.h
52+ struct MotifWmHints {
53+ MotifFlags flags = MotifFlags::MWM_HINTS_NONE;
54+ MotifFunctions functions = MotifFunctions::MWM_FUNC_NONE;
55+ MotifDecorations decorations = MotifDecorations::MWM_DECOR_NONE;
56+ uint32_t input_mode = 0 ; // unused
57+ uint32_t status = 0 ; // unused
58+ };
59+
60+ inline MotifWmHints createFlagsToMotifWmHints (IWindow::E_CREATE_FLAGS flags) {
61+ core::bitflag<MotifFlags> motifFlags (MWM_HINTS_NONE);
62+ core::bitflag<MotifFunctions> motifFunctions (MWM_FUNC_NONE);
63+ core::bitflag<MotifDecorations> motifDecorations (MWM_DECOR_NONE);
64+ motifFlags |= MWM_HINTS_DECORATIONS;
65+
66+ if (flags & IWindow::ECF_BORDERLESS) {
67+ motifDecorations |= MWM_DECOR_ALL;
68+ } else {
69+ motifDecorations |= MWM_DECOR_BORDER;
70+ motifDecorations |= MWM_DECOR_RESIZEH;
71+ motifDecorations |= MWM_DECOR_TITLE;
72+
73+ // minimize button
74+ if (flags & IWindow::ECF_MINIMIZED) {
75+ motifDecorations |= MWM_DECOR_MINIMIZE;
76+ motifFunctions |= MWM_FUNC_MINIMIZE;
77+ }
78+
79+ // maximize button
80+ if (flags & IWindow::ECF_MAXIMIZED) {
81+ motifDecorations |= MWM_DECOR_MAXIMIZE;
82+ motifFunctions |= MWM_FUNC_MAXIMIZE;
83+ }
84+
85+ // close button
86+ motifFunctions |= MWM_FUNC_CLOSE;
87+ }
88+
89+ if (motifFunctions.value != MWM_FUNC_NONE) {
90+ motifFlags |= MWM_HINTS_FUNCTIONS;
91+ motifFunctions |= MWM_FUNC_RESIZE;
92+ motifFunctions |= MWM_FUNC_MOVE;
93+ } else {
94+ motifFunctions = MWM_FUNC_ALL;
95+ }
96+
97+ MotifWmHints hints;
98+ hints.flags = motifFlags.value ;
99+ hints.functions = motifFunctions.value ;
100+ hints.decorations = motifDecorations.value ;
101+ hints.input_mode = 0 ;
102+ hints.status = 0 ;
103+ return hints;
104+ }
105+
106+ class XCBHandle final : public core::IReferenceCounted {
107107 public:
108- template <core::StringLiteral Name>
109- struct AtomToken {
110- private:
111- AtomToken (XCBHandle& handle) : m_handle(handle) {}
112- XCBHandle& m_handle;
113- xcb_atom_t m_token = 0 ;
114- bool fetched = false ;
115- public:
116- inline xcb_atom_t fetch (bool only_if_exists = true ,
117- bool forced = false ) {
118- const auto & xcb = m_handle.getXcbFunctionTable ();
119- if (fetched && !forced) {
120- return m_token;
121- }
122- fetched = true ;
123- size_t size = Name.size () - 1 ; // -1 to remove the null terminator
124- xcb_intern_atom_cookie_t cookie = xcb.pxcb_intern_atom (m_handle, only_if_exists, size, Name.value );
125- if (xcb_intern_atom_reply_t * reply = xcb.pxcb_intern_atom_reply (m_handle, cookie, nullptr )) {
126- m_token = reply->atom ;
127- free (reply);
128- return m_token;
129- }
130- return m_token;
131- }
132-
133- friend class XCBHandle ;
134- };
135-
108+ struct XCBHandleToken {
109+ private:
110+ xcb_atom_t m_token = 0 ;
111+ public:
112+ inline operator xcb_atom_t () { return m_token;}
113+ friend class XCBHandle ;
114+ };
115+
136116 XCBHandle (core::smart_refctd_ptr<IWindowManagerXCB>&& windowManager):
137117 m_windowManager (std::move(windowManager)) {
138118 const auto & xcb = m_windowManager->getXcbFunctionTable ();
139119 m_connection = xcb.pxcb_connect (nullptr , nullptr );
120+
121+ struct {
122+ const char * name;
123+ XCBHandleToken* token;
124+ } handles[] = {
125+ {" WM_DELETE_WINDOW" , &WM_DELETE_WINDOW},
126+ {" WM_PROTOCOLS" , &WM_PROTOCOLS},
127+ {" _NET_WM_PING" , &_NET_WM_PING},
128+ {" _NET_WM_STATE_MAXIMIZED_VERT" , &_NET_WM_STATE_MAXIMIZED_VERT},
129+ {" _NET_WM_STATE_MAXIMIZED_HORZ" , &_NET_WM_STATE_MAXIMIZED_HORZ},
130+ {" _NET_WM_STATE_FULLSCREEN" , &_NET_WM_STATE_FULLSCREEN},
131+ {" _NET_WM_STATE" , &_NET_WM_STATE},
132+ {" _MOTIF_WM_HINTS" , &_MOTIF_WM_HINTS},
133+ {" NET_WM_STATE_ABOVE" , &NET_WM_STATE_ABOVE},
134+ };
135+ std::array<xcb_intern_atom_cookie_t , std::size (handles)> cookies;
136+ for (size_t i = 0 ; i < std::size (handles); ++i) {
137+ cookies[i] = xcb.pxcb_intern_atom (m_connection, false , strlen (handles[i].name ), handles[i].name );
138+ }
139+ for (size_t i = 0 ; i < std::size (handles); ++i) {
140+ xcb_intern_atom_reply_t *reply = xcb.pxcb_intern_atom_reply (m_connection, cookies[i], nullptr );
141+ handles[i].token ->m_token = reply->atom ;
142+ free (reply);
143+ }
140144 }
141145 ~XCBHandle () {
142146 if (m_connection) {
143- const auto & xcb = m_windowManager->getXcbFunctionTable ();
147+ const auto & xcb = m_windowManager->getXcbFunctionTable ();
144148 xcb.pxcb_disconnect (m_connection);
145149 }
146150 }
@@ -149,79 +153,64 @@ namespace nbl::ui::xcb
149153 inline const IWindowManagerXCB::Xcb& getXcbFunctionTable () const { return m_windowManager->getXcbFunctionTable (); }
150154 inline const IWindowManagerXCB::XcbIcccm& getXcbIcccmFunctionTable () const { return m_windowManager->getXcbIcccmFunctionTable (); }
151155 inline operator xcb_connection_t *() { return m_connection; }
152- inline xcb_connection_t * getNativeHandle () { return m_connection; }
153-
154-
155- AtomToken<core::StringLiteral(" WM_DELETE_WINDOW" )> WM_DELETE_WINDOW{*this };
156- AtomToken<core::StringLiteral(" WM_PROTOCOLS" )> WM_PROTOCOLS{*this };
157- AtomToken<core::StringLiteral(" _NET_WM_PING" )> _NET_WM_PING{*this };
158-
159- AtomToken<core::StringLiteral(" _NET_WM_STATE_MAXIMIZED_VERT" )> _NET_WM_STATE_MAXIMIZED_VERT{*this };
160- AtomToken<core::StringLiteral(" _NET_WM_STATE_MAXIMIZED_HORZ" )> _NET_WM_STATE_MAXIMIZED_HORZ{*this };
161- AtomToken<core::StringLiteral(" _NET_WM_STATE_FULLSCREEN" )> _NET_WM_STATE_FULLSCREEN{*this };
162- AtomToken<core::StringLiteral(" _NET_WM_STATE" )> _NET_WM_STATE{*this };
163- AtomToken<core::StringLiteral(" _MOTIF_WM_HINTS" )> _MOTIF_WM_HINTS{*this };
164- AtomToken<core::StringLiteral(" NET_WM_STATE_ABOVE" )> NET_WM_STATE_ABOVE{*this };
165-
166- AtomToken<core::StringLiteral(" CLIPBOARD" )> m_CLIPBOARD{*this };
167- AtomToken<core::StringLiteral(" TARGETS" )> m_TARGETS{*this };
168- AtomToken<core::StringLiteral(" INCR" )> m_INCR{*this };
169-
170- AtomToken<core::StringLiteral(" UTF8_STRING" )> m_formatUTF8_0{*this };
171- AtomToken<core::StringLiteral(" text/plain;charset=utf-8" )> m_formatUTF8_1{*this };
172- AtomToken<core::StringLiteral(" text/plain;charset=UTF-8" )> m_formatUTF8_2{*this };
173- AtomToken<core::StringLiteral(" GTK_TEXT_BUFFER_CONTENTS" )> m_formatGTK{*this };
174- AtomToken<core::StringLiteral(" STRING" )> m_formatString{*this };
175- AtomToken<core::StringLiteral(" TEXT" )> m_formatText{*this };
176- AtomToken<core::StringLiteral(" text/plain" )> m_formatTextPlain{*this };
156+ inline xcb_connection_t * getNativeHandle () { return m_connection; }
157+
158+ XCBHandleToken WM_DELETE_WINDOW;
159+ XCBHandleToken WM_PROTOCOLS;
160+ XCBHandleToken _NET_WM_PING;
161+
162+ XCBHandleToken _NET_WM_STATE_MAXIMIZED_VERT;
163+ XCBHandleToken _NET_WM_STATE_MAXIMIZED_HORZ;
164+ XCBHandleToken _NET_WM_STATE_FULLSCREEN;
165+ XCBHandleToken _NET_WM_STATE;
166+ XCBHandleToken _MOTIF_WM_HINTS;
167+ XCBHandleToken NET_WM_STATE_ABOVE;
177168 private:
178169 core::smart_refctd_ptr<IWindowManagerXCB> m_windowManager;
179170 xcb_connection_t * m_connection = nullptr ;
180171 };
181172
182- inline void setMotifWmHints (XCBHandle& handle, xcb_window_t window, const MotifWmHints& hint) {
183- auto & xcb = handle.getXcbFunctionTable ();
184-
185- auto atomHint = handle._MOTIF_WM_HINTS .fetch ();
186- if (hint.flags != MotifFlags::MWM_HINTS_NONE) {
187- xcb.pxcb_change_property (handle.getNativeHandle (), XCB_PROP_MODE_REPLACE, window,
188- atomHint,
189- atomHint, 32 , sizeof (MotifWmHints) / sizeof (uint32_t ), &hint);
190- } else {
191- xcb.pxcb_delete_property (handle.getNativeHandle (), window, atomHint);
192- }
193- }
194-
195- inline void setNetMWState (XCBHandle& handle, xcb_window_t rootWindow,
196- xcb_window_t window,
197- bool set,
198- xcb_atom_t first,
199- xcb_atom_t second = XCB_NONE) {
200- auto & xcb = handle.getXcbFunctionTable ();
201-
202- xcb_client_message_event_t event;
203- event.response_type = XCB_CLIENT_MESSAGE;
204- event.type = handle._NET_WM_STATE .fetch ();
205- event.window = window;
206- event.format = 32 ;
207- event.sequence = 0 ;
208- event.data .data32 [0 ] = set ? 1l : 0l ;
209- event.data .data32 [1 ] = first;
210- event.data .data32 [2 ] = second;
211- event.data .data32 [3 ] = 1 ;
212- event.data .data32 [4 ] = 0 ;
213- xcb.pxcb_send_event (handle, 0 , rootWindow,
214- XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, reinterpret_cast <const char *>(&event));
215- }
216-
217- inline const xcb_screen_t * primaryScreen (XCBHandle& handle) {
218- auto & xcb = handle.getXcbFunctionTable ();
219- const xcb_setup_t *setup = xcb.pxcb_get_setup (handle);
220- xcb_screen_t *screen = xcb.pxcb_setup_roots_iterator (setup).data ;
221- return screen;
222- }
223- }
173+ inline void setMotifWmHints (XCBHandle& handle, xcb_window_t window, const MotifWmHints& hint) {
174+ auto & xcb = handle.getXcbFunctionTable ();
224175
176+ if (hint.flags != MotifFlags::MWM_HINTS_NONE) {
177+ xcb.pxcb_change_property (handle.getNativeHandle (), XCB_PROP_MODE_REPLACE, window,
178+ handle._MOTIF_WM_HINTS ,
179+ handle._MOTIF_WM_HINTS , 32 , sizeof (MotifWmHints) / sizeof (uint32_t ), &hint);
180+ } else {
181+ xcb.pxcb_delete_property (handle.getNativeHandle (), window, handle._MOTIF_WM_HINTS );
182+ }
183+ }
184+
185+ inline void setNetMWState (XCBHandle& handle, xcb_window_t rootWindow,
186+ xcb_window_t window,
187+ bool set,
188+ xcb_atom_t first,
189+ xcb_atom_t second = XCB_NONE) {
190+ auto & xcb = handle.getXcbFunctionTable ();
191+
192+ xcb_client_message_event_t event;
193+ event.response_type = XCB_CLIENT_MESSAGE;
194+ event.type = handle._NET_WM_STATE ;
195+ event.window = window;
196+ event.format = 32 ;
197+ event.sequence = 0 ;
198+ event.data .data32 [0 ] = set ? 1l : 0l ;
199+ event.data .data32 [1 ] = first;
200+ event.data .data32 [2 ] = second;
201+ event.data .data32 [3 ] = 1 ;
202+ event.data .data32 [4 ] = 0 ;
203+ xcb.pxcb_send_event (handle, 0 , rootWindow,
204+ XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, reinterpret_cast <const char *>(&event));
205+ }
206+
207+ inline const xcb_screen_t * primaryScreen (XCBHandle& handle) {
208+ auto & xcb = handle.getXcbFunctionTable ();
209+ const xcb_setup_t *setup = xcb.pxcb_get_setup (handle);
210+ xcb_screen_t *screen = xcb.pxcb_setup_roots_iterator (setup).data ;
211+ return screen;
212+ }
213+ }
225214
226215#endif
227216#endif // C_XCB_HANDLER_XCB
0 commit comments