style: spaces -> tabs
This commit is contained in:
		
							parent
							
								
									7b3700e730
								
							
						
					
					
						commit
						0f81338bb6
					
				
							
								
								
									
										36
									
								
								meson.build
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								meson.build
									
									
									
									
									
								
							| @ -1,9 +1,9 @@ | ||||
| project('somebar', ['c', 'cpp'], | ||||
|   version: '0.1.0', | ||||
|   default_options: [ | ||||
|     'cpp_std=c++17', | ||||
|     'cpp_args=-Wno-parentheses', | ||||
|   ]) | ||||
| 	version: '0.1.0', | ||||
| 	default_options: [ | ||||
| 	    'cpp_std=c++17', | ||||
| 	    'cpp_args=-Wno-parentheses', | ||||
| 	]) | ||||
| 
 | ||||
| wayland_dep = dependency('wayland-client') | ||||
| wayland_cursor_dep = dependency('wayland-cursor') | ||||
| @ -14,16 +14,16 @@ pangocairo_dep = dependency('pangocairo') | ||||
| subdir('protocols') | ||||
| 
 | ||||
| executable('somebar', | ||||
|   'src/main.cpp', | ||||
|   'src/shm_buffer.cpp', | ||||
|   'src/bar.cpp', | ||||
|   wayland_sources, | ||||
|   dependencies: [ | ||||
|     wayland_dep, | ||||
|     wayland_cursor_dep, | ||||
|     cairo_dep, | ||||
|     pango_dep, | ||||
|     pangocairo_dep, | ||||
|   ], | ||||
|   install: true, | ||||
|   cpp_args: '-DSOMEBAR_VERSION="@0@"'.format(meson.project_version())) | ||||
| 	'src/main.cpp', | ||||
| 	'src/shm_buffer.cpp', | ||||
| 	'src/bar.cpp', | ||||
| 	wayland_sources, | ||||
| 	dependencies: [ | ||||
| 	    wayland_dep, | ||||
| 	    wayland_cursor_dep, | ||||
| 	    cairo_dep, | ||||
| 	    pango_dep, | ||||
| 	    pangocairo_dep, | ||||
| 	], | ||||
| 	install: true, | ||||
| 	cpp_args: '-DSOMEBAR_VERSION="@0@"'.format(meson.project_version())) | ||||
|  | ||||
| @ -3,21 +3,21 @@ wayland_scanner = find_program('wayland-scanner') | ||||
| wayland_protos_dep = dependency('wayland-protocols') | ||||
| wl_protocol_dir = wayland_protos_dep.get_pkgconfig_variable('pkgdatadir') | ||||
| wayland_scanner_code = generator( | ||||
|   wayland_scanner, | ||||
|   output: '@BASENAME@-protocol.c', | ||||
|   arguments: ['private-code', '@INPUT@', '@OUTPUT@']) | ||||
| 	wayland_scanner, | ||||
| 	output: '@BASENAME@-protocol.c', | ||||
| 	arguments: ['private-code', '@INPUT@', '@OUTPUT@']) | ||||
| wayland_scanner_client = generator( | ||||
|   wayland_scanner, | ||||
|   output: '@BASENAME@-client-protocol.h', | ||||
|   arguments: ['client-header', '@INPUT@', '@OUTPUT@']) | ||||
| 	wayland_scanner, | ||||
| 	output: '@BASENAME@-client-protocol.h', | ||||
| 	arguments: ['client-header', '@INPUT@', '@OUTPUT@']) | ||||
| 
 | ||||
| wayland_xmls = [ | ||||
|   wl_protocol_dir + '/stable/xdg-shell/xdg-shell.xml', | ||||
|   wl_protocol_dir + '/unstable/xdg-output/xdg-output-unstable-v1.xml', | ||||
|   'wlr-layer-shell-unstable-v1.xml', | ||||
|   'net-tapesoftware-dwl-wm-unstable-v1.xml', | ||||
| 	wl_protocol_dir + '/stable/xdg-shell/xdg-shell.xml', | ||||
| 	wl_protocol_dir + '/unstable/xdg-output/xdg-output-unstable-v1.xml', | ||||
| 	'wlr-layer-shell-unstable-v1.xml', | ||||
| 	'net-tapesoftware-dwl-wm-unstable-v1.xml', | ||||
| ] | ||||
| wayland_sources = [ | ||||
|   wayland_scanner_code.process(wayland_xmls), | ||||
|   wayland_scanner_client.process(wayland_xmls), | ||||
| 	wayland_scanner_code.process(wayland_xmls), | ||||
| 	wayland_scanner_client.process(wayland_xmls), | ||||
| ] | ||||
|  | ||||
| @ -1,164 +1,164 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <protocol name="net_tapesoftware_dwl_wm_unstable_v1"> | ||||
|     <copyright> | ||||
|         Copyright (c) 2021 Raphael Robatsch | ||||
| 	<copyright> | ||||
| 		Copyright (c) 2021 Raphael Robatsch | ||||
| 
 | ||||
|         Permission is hereby granted, free of charge, to any person obtaining a | ||||
|         copy of this software and associated documentation files (the | ||||
|         "Software"), to deal in the Software without restriction, including | ||||
|         without limitation the rights to use, copy, modify, merge, publish, | ||||
|         distribute, sublicense, and/or sell copies of the Software, and to | ||||
|         permit persons to whom the Software is furnished to do so, subject to | ||||
|         the following conditions: | ||||
| 		Permission is hereby granted, free of charge, to any person obtaining a | ||||
| 		copy of this software and associated documentation files (the | ||||
| 		"Software"), to deal in the Software without restriction, including | ||||
| 		without limitation the rights to use, copy, modify, merge, publish, | ||||
| 		distribute, sublicense, and/or sell copies of the Software, and to | ||||
| 		permit persons to whom the Software is furnished to do so, subject to | ||||
| 		the following conditions: | ||||
| 
 | ||||
|         The above copyright notice and this permission notice (including the | ||||
|         next paragraph) shall be included in all copies or substantial portions | ||||
|         of the Software. | ||||
| 		The above copyright notice and this permission notice (including the | ||||
| 		next paragraph) shall be included in all copies or substantial portions | ||||
| 		of the Software. | ||||
| 
 | ||||
|         THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | ||||
|         OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||||
|         MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
|         IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||||
|         CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||||
|         TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||||
|         SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
|     </copyright> | ||||
| 		THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | ||||
| 		OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||||
| 		MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
| 		IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||||
| 		CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||||
| 		TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||||
| 		SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||
| 	</copyright> | ||||
| 
 | ||||
|     <interface name="znet_tapesoftware_dwl_wm_v1" version="1"> | ||||
|         <description summary="control the dwl state"> | ||||
|             This interface is exposed as a global in the wl_registry. | ||||
| 	<interface name="znet_tapesoftware_dwl_wm_v1" version="1"> | ||||
| 		<description summary="control the dwl state"> | ||||
| 			This interface is exposed as a global in the wl_registry. | ||||
| 
 | ||||
|             Clients can use this protocol to receive updates of the window manager | ||||
|             state (active tags, active layout, and focused window). | ||||
|             Clients can also control this state. | ||||
| 			Clients can use this protocol to receive updates of the window manager | ||||
| 			state (active tags, active layout, and focused window). | ||||
| 			Clients can also control this state. | ||||
| 
 | ||||
|             After binding, the client will receive the available tags and layouts | ||||
|             with the 'tag' and 'layout' events. These can be used in subsequent | ||||
|             dwl_wm_monitor_v1.set_tags/set_layout requests, and to interpret the | ||||
|             dwl_wm_monitor_v1.layout/tag events. | ||||
|         </description> | ||||
| 			After binding, the client will receive the available tags and layouts | ||||
| 			with the 'tag' and 'layout' events. These can be used in subsequent | ||||
| 			dwl_wm_monitor_v1.set_tags/set_layout requests, and to interpret the | ||||
| 			dwl_wm_monitor_v1.layout/tag events. | ||||
| 		</description> | ||||
| 
 | ||||
|         <request name="release" type="destructor"> | ||||
|             <description summary="release dwl_wm"> | ||||
|                 This request indicates that the client will not use the dwl_wm | ||||
|                 object any more. Objects that have been created through this instance | ||||
|                 are not affected. | ||||
|             </description> | ||||
|         </request> | ||||
| 		<request name="release" type="destructor"> | ||||
| 			<description summary="release dwl_wm"> | ||||
| 				This request indicates that the client will not use the dwl_wm | ||||
| 				object any more. Objects that have been created through this instance | ||||
| 				are not affected. | ||||
| 			</description> | ||||
| 		</request> | ||||
| 
 | ||||
|         <request name="get_monitor"> | ||||
|             <description summary="gets a dwl monitor from an output"> | ||||
|                 Gets a dwl monitor for the specified output. The window manager | ||||
|                 state on the output can be controlled using the monitor. | ||||
|             </description> | ||||
|             <arg name="id" type="new_id" interface="znet_tapesoftware_dwl_wm_monitor_v1" /> | ||||
|             <arg name="output" type="object" interface="wl_output" /> | ||||
|         </request> | ||||
| 		<request name="get_monitor"> | ||||
| 			<description summary="gets a dwl monitor from an output"> | ||||
| 				Gets a dwl monitor for the specified output. The window manager | ||||
| 				state on the output can be controlled using the monitor. | ||||
| 			</description> | ||||
| 			<arg name="id" type="new_id" interface="znet_tapesoftware_dwl_wm_monitor_v1" /> | ||||
| 			<arg name="output" type="object" interface="wl_output" /> | ||||
| 		</request> | ||||
| 
 | ||||
|         <event name="tag"> | ||||
|             <description summary="announces the presence of a tag"> | ||||
|                 This event is sent immediately after binding. | ||||
|                 A roundtrip after binding guarantees that the client has received all tags. | ||||
|             </description> | ||||
|             <arg name="name" type="string"/> | ||||
|         </event> | ||||
| 		<event name="tag"> | ||||
| 			<description summary="announces the presence of a tag"> | ||||
| 				This event is sent immediately after binding. | ||||
| 				A roundtrip after binding guarantees that the client has received all tags. | ||||
| 			</description> | ||||
| 			<arg name="name" type="string"/> | ||||
| 		</event> | ||||
| 
 | ||||
|         <event name="layout"> | ||||
|             <description summary="announces the presence of a layout"> | ||||
|                 This event is sent immediately after binding. | ||||
|                 A roundtrip after binding guarantees that the client has received all layouts. | ||||
|             </description> | ||||
|             <arg name="name" type="string"/> | ||||
|         </event> | ||||
|     </interface> | ||||
| 		<event name="layout"> | ||||
| 			<description summary="announces the presence of a layout"> | ||||
| 				This event is sent immediately after binding. | ||||
| 				A roundtrip after binding guarantees that the client has received all layouts. | ||||
| 			</description> | ||||
| 			<arg name="name" type="string"/> | ||||
| 		</event> | ||||
| 	</interface> | ||||
| 
 | ||||
|     <interface name="znet_tapesoftware_dwl_wm_monitor_v1" version="1"> | ||||
|         <description summary="control one monitor"> | ||||
|             Observes and controls one monitor. | ||||
| 	<interface name="znet_tapesoftware_dwl_wm_monitor_v1" version="1"> | ||||
| 		<description summary="control one monitor"> | ||||
| 			Observes and controls one monitor. | ||||
| 
 | ||||
|             Events are double-buffered: Clients should cache all events and only | ||||
|             redraw themselves once the 'frame' event is sent. | ||||
| 			Events are double-buffered: Clients should cache all events and only | ||||
| 			redraw themselves once the 'frame' event is sent. | ||||
| 
 | ||||
|             Requests are not double-buffered: The compositor will update itself | ||||
|             immediately. | ||||
|         </description> | ||||
| 			Requests are not double-buffered: The compositor will update itself | ||||
| 			immediately. | ||||
| 		</description> | ||||
| 
 | ||||
|         <enum name="tag_state"> | ||||
|             <entry name="none" value="0" summary="no state"/> | ||||
|             <entry name="active" value="1" summary="tag is active"/> | ||||
|             <entry name="urgent" value="2" summary="tag has at least one urgent client"/> | ||||
|         </enum> | ||||
| 		<enum name="tag_state"> | ||||
| 			<entry name="none" value="0" summary="no state"/> | ||||
| 			<entry name="active" value="1" summary="tag is active"/> | ||||
| 			<entry name="urgent" value="2" summary="tag has at least one urgent client"/> | ||||
| 		</enum> | ||||
| 
 | ||||
|         <request name="release" type="destructor"> | ||||
|             <description summary="release dwl_monitor"> | ||||
|                 This request indicates that the client is done with this dwl_monitor. | ||||
|                 All further requests are ignored. | ||||
|             </description> | ||||
|         </request> | ||||
| 		<request name="release" type="destructor"> | ||||
| 			<description summary="release dwl_monitor"> | ||||
| 				This request indicates that the client is done with this dwl_monitor. | ||||
| 				All further requests are ignored. | ||||
| 			</description> | ||||
| 		</request> | ||||
| 
 | ||||
|         <event name="selected"> | ||||
|             <description summary="updates the selected state of the monitor"> | ||||
|                 If 'selected' is nonzero, this monitor is the currently selected one. | ||||
|             </description> | ||||
|             <arg name="selected" type="uint"/> | ||||
|         </event> | ||||
| 		<event name="selected"> | ||||
| 			<description summary="updates the selected state of the monitor"> | ||||
| 				If 'selected' is nonzero, this monitor is the currently selected one. | ||||
| 			</description> | ||||
| 			<arg name="selected" type="uint"/> | ||||
| 		</event> | ||||
| 
 | ||||
|         <event name="tag"> | ||||
|             <description summary="updates the state of one tag"> | ||||
|                 Announces the update of a tag. num_clients and focused_client can be | ||||
|                 used to draw client indicators. | ||||
|             </description> | ||||
|             <arg name="tag" type="uint" summary="index of a tag received by the dwl_wm_v1.tag event." /> | ||||
|             <arg name="state" type="uint" enum="tag_state"/> | ||||
|             <arg name="num_clients" type="uint" summary="number of clients on this tag"/> | ||||
|             <arg name="focused_client" type="int" summary="out of num_clients. -1 if there is no focused client"/> | ||||
|         </event> | ||||
| 		<event name="tag"> | ||||
| 			<description summary="updates the state of one tag"> | ||||
| 				Announces the update of a tag. num_clients and focused_client can be | ||||
| 				used to draw client indicators. | ||||
| 			</description> | ||||
| 			<arg name="tag" type="uint" summary="index of a tag received by the dwl_wm_v1.tag event." /> | ||||
| 			<arg name="state" type="uint" enum="tag_state"/> | ||||
| 			<arg name="num_clients" type="uint" summary="number of clients on this tag"/> | ||||
| 			<arg name="focused_client" type="int" summary="out of num_clients. -1 if there is no focused client"/> | ||||
| 		</event> | ||||
| 
 | ||||
|         <event name="layout"> | ||||
|             <description summary="updates the selected layout"> | ||||
|                 Announces the update of the selected layout. | ||||
|             </description> | ||||
|             <arg name="layout" type="uint" summary="index of a layout received by the dwl_wm_v1.layout event."/> | ||||
|         </event> | ||||
| 		<event name="layout"> | ||||
| 			<description summary="updates the selected layout"> | ||||
| 				Announces the update of the selected layout. | ||||
| 			</description> | ||||
| 			<arg name="layout" type="uint" summary="index of a layout received by the dwl_wm_v1.layout event."/> | ||||
| 		</event> | ||||
| 
 | ||||
|         <event name="title"> | ||||
|             <description summary="updates the focused client"> | ||||
|                 Announces the update of the selected client. | ||||
|             </description> | ||||
|             <arg name="title" type="string"/> | ||||
|         </event> | ||||
| 		<event name="title"> | ||||
| 			<description summary="updates the focused client"> | ||||
| 				Announces the update of the selected client. | ||||
| 			</description> | ||||
| 			<arg name="title" type="string"/> | ||||
| 		</event> | ||||
| 
 | ||||
|         <event name="frame"> | ||||
|             <description summary="end of status update sequence"> | ||||
|                 Sent after all other events belonging to the status update has been sent. | ||||
|                 Clients should redraw themselves now. | ||||
|             </description> | ||||
|         </event> | ||||
| 		<event name="frame"> | ||||
| 			<description summary="end of status update sequence"> | ||||
| 				Sent after all other events belonging to the status update has been sent. | ||||
| 				Clients should redraw themselves now. | ||||
| 			</description> | ||||
| 		</event> | ||||
| 
 | ||||
|         <request name="set_tags"> | ||||
|             <description summary="sets the active tags on this monitor."> | ||||
|                 Changes are applied immediately. | ||||
|             </description> | ||||
|             <arg name="tagmask" type="uint" summary="bitmask of the tags that should be set."/> | ||||
|             <arg name="toggle_tagset" type="uint"/> | ||||
|         </request> | ||||
| 		<request name="set_tags"> | ||||
| 			<description summary="sets the active tags on this monitor."> | ||||
| 				Changes are applied immediately. | ||||
| 			</description> | ||||
| 			<arg name="tagmask" type="uint" summary="bitmask of the tags that should be set."/> | ||||
| 			<arg name="toggle_tagset" type="uint"/> | ||||
| 		</request> | ||||
| 
 | ||||
|         <request name="set_client_tags"> | ||||
|             <description summary="updates the tags of the focused client."> | ||||
|                 tags are updated as follows: | ||||
|                 new_tags = (current_tags AND and_tags) XOR xor_tags | ||||
| 		<request name="set_client_tags"> | ||||
| 			<description summary="updates the tags of the focused client."> | ||||
| 				tags are updated as follows: | ||||
| 				new_tags = (current_tags AND and_tags) XOR xor_tags | ||||
| 
 | ||||
|                 Changes are applied immediately. | ||||
|             </description> | ||||
|             <arg name="and_tags" type="uint"/> | ||||
|             <arg name="xor_tags" type="uint"/> | ||||
|         </request> | ||||
| 				Changes are applied immediately. | ||||
| 			</description> | ||||
| 			<arg name="and_tags" type="uint"/> | ||||
| 			<arg name="xor_tags" type="uint"/> | ||||
| 		</request> | ||||
| 
 | ||||
|         <request name="set_layout"> | ||||
|             <description summary="sets the active layout on this monitor."> | ||||
|                 Changes are applied immediately. | ||||
|             </description> | ||||
|             <arg name="layout" type="uint" summary="index of a layout received by the dwl_wm_v1.layout event."/> | ||||
|         </request> | ||||
|     </interface> | ||||
| 		<request name="set_layout"> | ||||
| 			<description summary="sets the active layout on this monitor."> | ||||
| 				Changes are applied immediately. | ||||
| 			</description> | ||||
| 			<arg name="layout" type="uint" summary="index of a layout received by the dwl_wm_v1.layout event."/> | ||||
| 		</request> | ||||
| 	</interface> | ||||
| </protocol> | ||||
|  | ||||
							
								
								
									
										316
									
								
								src/bar.cpp
									
									
									
									
									
								
							
							
						
						
									
										316
									
								
								src/bar.cpp
									
									
									
									
									
								
							| @ -11,44 +11,44 @@ | ||||
| #include "pango/pango-layout.h" | ||||
| 
 | ||||
| const zwlr_layer_surface_v1_listener Bar::_layerSurfaceListener = { | ||||
|     [](void* owner, zwlr_layer_surface_v1*, uint32_t serial, uint32_t width, uint32_t height) | ||||
|     { | ||||
|         static_cast<Bar*>(owner)->layerSurfaceConfigure(serial, width, height); | ||||
|     } | ||||
| 	[](void* owner, zwlr_layer_surface_v1*, uint32_t serial, uint32_t width, uint32_t height) | ||||
| 	{ | ||||
| 		static_cast<Bar*>(owner)->layerSurfaceConfigure(serial, width, height); | ||||
| 	} | ||||
| }; | ||||
| const wl_callback_listener Bar::_frameListener = { | ||||
|     [](void* owner, wl_callback* cb, uint32_t) | ||||
|     { | ||||
|         static_cast<Bar*>(owner)->render(); | ||||
|         wl_callback_destroy(cb); | ||||
|     } | ||||
| 	[](void* owner, wl_callback* cb, uint32_t) | ||||
| 	{ | ||||
| 		static_cast<Bar*>(owner)->render(); | ||||
| 		wl_callback_destroy(cb); | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| struct Font { | ||||
|     PangoFontDescription* description; | ||||
|     int height {0}; | ||||
| 	PangoFontDescription* description; | ||||
| 	int height {0}; | ||||
| }; | ||||
| static Font getFont() | ||||
| { | ||||
|     auto fontMap = pango_cairo_font_map_get_default(); | ||||
|     if (!fontMap) die("pango_cairo_font_map_get_default"); | ||||
|     auto fontDesc = pango_font_description_from_string(font); | ||||
|     if (!fontDesc) die("pango_font_description_from_string"); | ||||
|     auto tempContext = pango_font_map_create_context(fontMap); | ||||
|     if (!tempContext) die("pango_font_map_create_context"); | ||||
|     auto font = pango_font_map_load_font(fontMap, tempContext, fontDesc); | ||||
|     if (!font) die("pango_font_map_load_font"); | ||||
|     auto metrics = pango_font_get_metrics(font, pango_language_get_default()); | ||||
|     if (!metrics) die("pango_font_get_metrics"); | ||||
| 	auto fontMap = pango_cairo_font_map_get_default(); | ||||
| 	if (!fontMap) die("pango_cairo_font_map_get_default"); | ||||
| 	auto fontDesc = pango_font_description_from_string(font); | ||||
| 	if (!fontDesc) die("pango_font_description_from_string"); | ||||
| 	auto tempContext = pango_font_map_create_context(fontMap); | ||||
| 	if (!tempContext) die("pango_font_map_create_context"); | ||||
| 	auto font = pango_font_map_load_font(fontMap, tempContext, fontDesc); | ||||
| 	if (!font) die("pango_font_map_load_font"); | ||||
| 	auto metrics = pango_font_get_metrics(font, pango_language_get_default()); | ||||
| 	if (!metrics) die("pango_font_get_metrics"); | ||||
| 
 | ||||
|     auto res = Font {}; | ||||
|     res.description = fontDesc; | ||||
|     res.height = PANGO_PIXELS(pango_font_metrics_get_height(metrics)); | ||||
| 	auto res = Font {}; | ||||
| 	res.description = fontDesc; | ||||
| 	res.height = PANGO_PIXELS(pango_font_metrics_get_height(metrics)); | ||||
| 
 | ||||
|     pango_font_metrics_unref(metrics); | ||||
|     g_object_unref(font); | ||||
|     g_object_unref(tempContext); | ||||
|     return res; | ||||
| 	pango_font_metrics_unref(metrics); | ||||
| 	g_object_unref(font); | ||||
| 	g_object_unref(tempContext); | ||||
| 	return res; | ||||
| } | ||||
| static Font barfont = getFont(); | ||||
| 
 | ||||
| @ -56,27 +56,27 @@ BarComponent::BarComponent() { } | ||||
| BarComponent::BarComponent(wl_unique_ptr<PangoLayout> layout) : pangoLayout {std::move(layout)} {} | ||||
| int BarComponent::width() const | ||||
| { | ||||
|     int w, h; | ||||
|     pango_layout_get_size(pangoLayout.get(), &w, &h); | ||||
|     return PANGO_PIXELS(w); | ||||
| 	int w, h; | ||||
| 	pango_layout_get_size(pangoLayout.get(), &w, &h); | ||||
| 	return PANGO_PIXELS(w); | ||||
| } | ||||
| void BarComponent::setText(const std::string& text) | ||||
| { | ||||
|     _text = std::make_unique<std::string>(text); | ||||
|     pango_layout_set_text(pangoLayout.get(), _text->c_str(), _text->size()); | ||||
| 	_text = std::make_unique<std::string>(text); | ||||
| 	pango_layout_set_text(pangoLayout.get(), _text->c_str(), _text->size()); | ||||
| } | ||||
| 
 | ||||
| Bar::Bar(Monitor* mon) | ||||
| { | ||||
|     _mon = mon; | ||||
|     _pangoContext.reset(pango_font_map_create_context(pango_cairo_font_map_get_default())); | ||||
|     if (!_pangoContext) die("pango_font_map_create_context"); | ||||
|     for (auto i=0u; i<tagNames.size(); i++) { | ||||
|         _tags.push_back({ ZNET_TAPESOFTWARE_DWL_WM_MONITOR_V1_TAG_STATE_NONE, 0, 0, createComponent(tagNames[i]) }); | ||||
|     } | ||||
|     _layoutCmp = createComponent(); | ||||
|     _titleCmp = createComponent(); | ||||
|     _statusCmp = createComponent(); | ||||
| 	_mon = mon; | ||||
| 	_pangoContext.reset(pango_font_map_create_context(pango_cairo_font_map_get_default())); | ||||
| 	if (!_pangoContext) die("pango_font_map_create_context"); | ||||
| 	for (auto i=0u; i<tagNames.size(); i++) { | ||||
| 		_tags.push_back({ ZNET_TAPESOFTWARE_DWL_WM_MONITOR_V1_TAG_STATE_NONE, 0, 0, createComponent(tagNames[i]) }); | ||||
| 	} | ||||
| 	_layoutCmp = createComponent(); | ||||
| 	_titleCmp = createComponent(); | ||||
| 	_statusCmp = createComponent(); | ||||
| } | ||||
| 
 | ||||
| const wl_surface* Bar::surface() const { return _surface.get(); } | ||||
| @ -84,35 +84,35 @@ bool Bar::visible() const { return _surface.get(); } | ||||
| 
 | ||||
| void Bar::show(wl_output* output) | ||||
| { | ||||
|     if (visible()) return; | ||||
|     _surface.reset(wl_compositor_create_surface(compositor)); | ||||
|     _layerSurface.reset(zwlr_layer_shell_v1_get_layer_surface(wlrLayerShell, | ||||
|         _surface.get(), nullptr, ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "net.tapesoftware.Somebar")); | ||||
|     zwlr_layer_surface_v1_add_listener(_layerSurface.get(), &_layerSurfaceListener, this); | ||||
|     auto anchor = topbar ? ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP : ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; | ||||
|     zwlr_layer_surface_v1_set_anchor(_layerSurface.get(), | ||||
|         anchor | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT); | ||||
| 	if (visible()) return; | ||||
| 	_surface.reset(wl_compositor_create_surface(compositor)); | ||||
| 	_layerSurface.reset(zwlr_layer_shell_v1_get_layer_surface(wlrLayerShell, | ||||
| 	_surface.get(), nullptr, ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "net.tapesoftware.Somebar")); | ||||
| 	zwlr_layer_surface_v1_add_listener(_layerSurface.get(), &_layerSurfaceListener, this); | ||||
| 	auto anchor = topbar ? ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP : ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; | ||||
| 	zwlr_layer_surface_v1_set_anchor(_layerSurface.get(), | ||||
| 	anchor | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT); | ||||
| 
 | ||||
|     auto barSize = barfont.height + paddingY * 2; | ||||
|     zwlr_layer_surface_v1_set_size(_layerSurface.get(), 0, barSize); | ||||
|     zwlr_layer_surface_v1_set_exclusive_zone(_layerSurface.get(), barSize); | ||||
|     wl_surface_commit(_surface.get()); | ||||
| 	auto barSize = barfont.height + paddingY * 2; | ||||
| 	zwlr_layer_surface_v1_set_size(_layerSurface.get(), 0, barSize); | ||||
| 	zwlr_layer_surface_v1_set_exclusive_zone(_layerSurface.get(), barSize); | ||||
| 	wl_surface_commit(_surface.get()); | ||||
| } | ||||
| 
 | ||||
| void Bar::hide() | ||||
| { | ||||
|     if (!visible()) return; | ||||
|     _layerSurface.reset(); | ||||
|     _surface.reset(); | ||||
|     _bufs.reset(); | ||||
| 	if (!visible()) return; | ||||
| 	_layerSurface.reset(); | ||||
| 	_surface.reset(); | ||||
| 	_bufs.reset(); | ||||
| } | ||||
| 
 | ||||
| void Bar::setTag(int tag, znet_tapesoftware_dwl_wm_monitor_v1_tag_state state, int numClients, int focusedClient) | ||||
| { | ||||
|     auto& t = _tags[tag]; | ||||
|     t.state = state; | ||||
|     t.numClients = numClients; | ||||
|     t.focusedClient = focusedClient; | ||||
| 	auto& t = _tags[tag]; | ||||
| 	t.state = state; | ||||
| 	t.numClients = numClients; | ||||
| 	t.focusedClient = focusedClient; | ||||
| } | ||||
| void Bar::setSelected(bool selected) { _selected = selected; } | ||||
| void Bar::setLayout(int layout) { _layoutCmp.setText(layoutNames[layout]); } | ||||
| @ -121,142 +121,142 @@ void Bar::setStatus(const std::string& status) { _statusCmp.setText(status); } | ||||
| 
 | ||||
| void Bar::invalidate() | ||||
| { | ||||
|     if (_invalid || !visible()) return; | ||||
|     _invalid = true; | ||||
|     auto frame = wl_surface_frame(_surface.get()); | ||||
|     wl_callback_add_listener(frame, &_frameListener, this); | ||||
|     wl_surface_commit(_surface.get()); | ||||
| 	if (_invalid || !visible()) return; | ||||
| 	_invalid = true; | ||||
| 	auto frame = wl_surface_frame(_surface.get()); | ||||
| 	wl_callback_add_listener(frame, &_frameListener, this); | ||||
| 	wl_surface_commit(_surface.get()); | ||||
| } | ||||
| 
 | ||||
| void Bar::click(int x, int, int btn) | ||||
| { | ||||
|     Arg arg = {0}; | ||||
|     Arg* argp = nullptr; | ||||
|     int control = ClkNone; | ||||
|     if (x > _statusCmp.x) { | ||||
|         control = ClkStatusText; | ||||
|     } else if (x > _titleCmp.x) { | ||||
|         control = ClkWinTitle; | ||||
|     } else if (x > _layoutCmp.x) { | ||||
|         control = ClkLayoutSymbol; | ||||
|     } else for (auto tag = _tags.size()-1; tag >= 0; tag--) { | ||||
|         if (x > _tags[tag].component.x) { | ||||
|             control = ClkTagBar; | ||||
|             arg.ui = 1<<tag; | ||||
|             argp = &arg; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     for (auto i = 0u; i < sizeof(buttons)/sizeof(buttons[0]); i++) { | ||||
|         const auto& button = buttons[i]; | ||||
|         if (button.control == control && button.btn == btn) { | ||||
|             button.func(*_mon, *(argp ? argp : &button.arg)); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
| 	Arg arg = {0}; | ||||
| 	Arg* argp = nullptr; | ||||
| 	int control = ClkNone; | ||||
| 	if (x > _statusCmp.x) { | ||||
| 		control = ClkStatusText; | ||||
| 	} else if (x > _titleCmp.x) { | ||||
| 		control = ClkWinTitle; | ||||
| 	} else if (x > _layoutCmp.x) { | ||||
| 		control = ClkLayoutSymbol; | ||||
| 	} else for (auto tag = _tags.size()-1; tag >= 0; tag--) { | ||||
| 		if (x > _tags[tag].component.x) { | ||||
| 			control = ClkTagBar; | ||||
| 			arg.ui = 1<<tag; | ||||
| 			argp = &arg; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	for (auto i = 0u; i < sizeof(buttons)/sizeof(buttons[0]); i++) { | ||||
| 		const auto& button = buttons[i]; | ||||
| 		if (button.control == control && button.btn == btn) { | ||||
| 			button.func(*_mon, *(argp ? argp : &button.arg)); | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void Bar::layerSurfaceConfigure(uint32_t serial, uint32_t width, uint32_t height) | ||||
| { | ||||
|     zwlr_layer_surface_v1_ack_configure(_layerSurface.get(), serial); | ||||
|     _bufs.emplace(width, height, WL_SHM_FORMAT_XRGB8888); | ||||
|     render(); | ||||
| 	zwlr_layer_surface_v1_ack_configure(_layerSurface.get(), serial); | ||||
| 	_bufs.emplace(width, height, WL_SHM_FORMAT_XRGB8888); | ||||
| 	render(); | ||||
| } | ||||
| 
 | ||||
| void Bar::render() | ||||
| { | ||||
|     if (!visible()) return; | ||||
|     auto img = wl_unique_ptr<cairo_surface_t> {cairo_image_surface_create_for_data( | ||||
|         _bufs->data(), | ||||
|         CAIRO_FORMAT_ARGB32, | ||||
|         _bufs->width, | ||||
|         _bufs->height, | ||||
|         _bufs->stride | ||||
|     )}; | ||||
|     auto painter = wl_unique_ptr<cairo_t> {cairo_create(img.get())}; | ||||
|     _painter = painter.get(); | ||||
|     pango_cairo_update_context(_painter, _pangoContext.get()); | ||||
|     _x = 0; | ||||
| 	if (!visible()) return; | ||||
| 	auto img = wl_unique_ptr<cairo_surface_t> {cairo_image_surface_create_for_data( | ||||
| 		_bufs->data(), | ||||
| 		CAIRO_FORMAT_ARGB32, | ||||
| 		_bufs->width, | ||||
| 		_bufs->height, | ||||
| 		_bufs->stride | ||||
| 		)}; | ||||
| 	auto painter = wl_unique_ptr<cairo_t> {cairo_create(img.get())}; | ||||
| 	_painter = painter.get(); | ||||
| 	pango_cairo_update_context(_painter, _pangoContext.get()); | ||||
| 	_x = 0; | ||||
| 
 | ||||
|     renderTags(); | ||||
|     setColorScheme(_selected ? colorActive : colorInactive); | ||||
|     renderComponent(_layoutCmp); | ||||
|     renderComponent(_titleCmp); | ||||
|     renderStatus(); | ||||
| 	renderTags(); | ||||
| 	setColorScheme(_selected ? colorActive : colorInactive); | ||||
| 	renderComponent(_layoutCmp); | ||||
| 	renderComponent(_titleCmp); | ||||
| 	renderStatus(); | ||||
| 
 | ||||
|     _painter = nullptr; | ||||
|     wl_surface_attach(_surface.get(), _bufs->buffer(), 0, 0); | ||||
|     wl_surface_damage(_surface.get(), 0, 0, INT_MAX, INT_MAX); | ||||
|     wl_surface_commit(_surface.get()); | ||||
|     _bufs->flip(); | ||||
|     _invalid = false; | ||||
| 	_painter = nullptr; | ||||
| 	wl_surface_attach(_surface.get(), _bufs->buffer(), 0, 0); | ||||
| 	wl_surface_damage(_surface.get(), 0, 0, INT_MAX, INT_MAX); | ||||
| 	wl_surface_commit(_surface.get()); | ||||
| 	_bufs->flip(); | ||||
| 	_invalid = false; | ||||
| } | ||||
| 
 | ||||
| void Bar::renderTags() | ||||
| { | ||||
|     for (auto &tag : _tags) { | ||||
|         setColorScheme( | ||||
|             tag.state & ZNET_TAPESOFTWARE_DWL_WM_MONITOR_V1_TAG_STATE_ACTIVE ? colorActive : colorInactive, | ||||
|             tag.state & ZNET_TAPESOFTWARE_DWL_WM_MONITOR_V1_TAG_STATE_URGENT); | ||||
|         renderComponent(tag.component); | ||||
|         auto indicators = std::min(tag.numClients, _bufs->height/2); | ||||
|         for (auto ind = 0; ind < indicators; ind++) { | ||||
|             auto w = ind == tag.focusedClient ? 7 : 1; | ||||
|             cairo_move_to(_painter, tag.component.x, ind*2+0.5); | ||||
|             cairo_rel_line_to(_painter, w, 0); | ||||
|             cairo_close_path(_painter); | ||||
|             cairo_set_line_width(_painter, 1); | ||||
|             cairo_stroke(_painter); | ||||
|         } | ||||
|     } | ||||
| 	for (auto &tag : _tags) { | ||||
| 		setColorScheme( | ||||
| 		tag.state & ZNET_TAPESOFTWARE_DWL_WM_MONITOR_V1_TAG_STATE_ACTIVE ? colorActive : colorInactive, | ||||
| 		tag.state & ZNET_TAPESOFTWARE_DWL_WM_MONITOR_V1_TAG_STATE_URGENT); | ||||
| 		renderComponent(tag.component); | ||||
| 		auto indicators = std::min(tag.numClients, _bufs->height/2); | ||||
| 		for (auto ind = 0; ind < indicators; ind++) { | ||||
| 			auto w = ind == tag.focusedClient ? 7 : 1; | ||||
| 			cairo_move_to(_painter, tag.component.x, ind*2+0.5); | ||||
| 			cairo_rel_line_to(_painter, w, 0); | ||||
| 			cairo_close_path(_painter); | ||||
| 			cairo_set_line_width(_painter, 1); | ||||
| 			cairo_stroke(_painter); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void Bar::renderStatus() | ||||
| { | ||||
|     pango_cairo_update_layout(_painter, _statusCmp.pangoLayout.get()); | ||||
|     beginBg(); | ||||
|     auto start = _bufs->width - _statusCmp.width() - paddingX*2; | ||||
|     cairo_rectangle(_painter, _x, 0, _bufs->width-_x+start, _bufs->height); | ||||
|     cairo_fill(_painter); | ||||
| 	pango_cairo_update_layout(_painter, _statusCmp.pangoLayout.get()); | ||||
| 	beginBg(); | ||||
| 	auto start = _bufs->width - _statusCmp.width() - paddingX*2; | ||||
| 	cairo_rectangle(_painter, _x, 0, _bufs->width-_x+start, _bufs->height); | ||||
| 	cairo_fill(_painter); | ||||
| 
 | ||||
|     _x = start; | ||||
|     renderComponent(_statusCmp); | ||||
| 	_x = start; | ||||
| 	renderComponent(_statusCmp); | ||||
| } | ||||
| 
 | ||||
| void Bar::setColorScheme(const ColorScheme& scheme, bool invert) | ||||
| { | ||||
|     _colorScheme = invert | ||||
|         ? ColorScheme {scheme.bg, scheme.fg} | ||||
|         : ColorScheme {scheme.fg, scheme.bg}; | ||||
| 	_colorScheme = invert | ||||
| 		? ColorScheme {scheme.bg, scheme.fg} | ||||
| 		: ColorScheme {scheme.fg, scheme.bg}; | ||||
| } | ||||
| static void setColor(cairo_t* painter, const Color& color) | ||||
| { | ||||
|     cairo_set_source_rgba(painter, color.r/255.0, color.g/255.0, color.b/255.0, color.a/255.0); | ||||
| 	cairo_set_source_rgba(painter, color.r/255.0, color.g/255.0, color.b/255.0, color.a/255.0); | ||||
| } | ||||
| void Bar::beginFg() { setColor(_painter, _colorScheme.fg); } | ||||
| void Bar::beginBg() { setColor(_painter, _colorScheme.bg); } | ||||
| 
 | ||||
| void Bar::renderComponent(BarComponent& component) | ||||
| { | ||||
|     pango_cairo_update_layout(_painter, component.pangoLayout.get()); | ||||
|     auto size = component.width() + paddingX*2; | ||||
|     component.x = _x; | ||||
| 	pango_cairo_update_layout(_painter, component.pangoLayout.get()); | ||||
| 	auto size = component.width() + paddingX*2; | ||||
| 	component.x = _x; | ||||
| 
 | ||||
|     beginBg(); | ||||
|     cairo_rectangle(_painter, _x, 0, size, _bufs->height); | ||||
|     cairo_fill(_painter); | ||||
|     cairo_move_to(_painter, _x+paddingX, paddingY); | ||||
| 	beginBg(); | ||||
| 	cairo_rectangle(_painter, _x, 0, size, _bufs->height); | ||||
| 	cairo_fill(_painter); | ||||
| 	cairo_move_to(_painter, _x+paddingX, paddingY); | ||||
| 
 | ||||
|     beginFg(); | ||||
|     pango_cairo_show_layout(_painter, component.pangoLayout.get()); | ||||
|     _x += size; | ||||
| 	beginFg(); | ||||
| 	pango_cairo_show_layout(_painter, component.pangoLayout.get()); | ||||
| 	_x += size; | ||||
| } | ||||
| 
 | ||||
| BarComponent Bar::createComponent(const std::string &initial) | ||||
| { | ||||
|     auto layout = pango_layout_new(_pangoContext.get()); | ||||
|     pango_layout_set_font_description(layout, barfont.description); | ||||
|     auto res = BarComponent {wl_unique_ptr<PangoLayout> {layout}}; | ||||
|     res.setText(initial); | ||||
|     return res; | ||||
| 	auto layout = pango_layout_new(_pangoContext.get()); | ||||
| 	pango_layout_set_font_description(layout, barfont.description); | ||||
| 	auto res = BarComponent {wl_unique_ptr<PangoLayout> {layout}}; | ||||
| 	res.setText(initial); | ||||
| 	return res; | ||||
| } | ||||
|  | ||||
							
								
								
									
										96
									
								
								src/bar.hpp
									
									
									
									
									
								
							
							
						
						
									
										96
									
								
								src/bar.hpp
									
									
									
									
									
								
							| @ -11,65 +11,65 @@ | ||||
| #include "shm_buffer.hpp" | ||||
| 
 | ||||
| class BarComponent { | ||||
|     std::unique_ptr<std::string> _text; | ||||
| 	std::unique_ptr<std::string> _text; | ||||
| public: | ||||
|     BarComponent(); | ||||
|     explicit BarComponent(wl_unique_ptr<PangoLayout> layout); | ||||
|     int width() const; | ||||
|     void setText(const std::string& text); | ||||
|     wl_unique_ptr<PangoLayout> pangoLayout; | ||||
|     int x {0}; | ||||
| 	BarComponent(); | ||||
| 	explicit BarComponent(wl_unique_ptr<PangoLayout> layout); | ||||
| 	int width() const; | ||||
| 	void setText(const std::string& text); | ||||
| 	wl_unique_ptr<PangoLayout> pangoLayout; | ||||
| 	int x {0}; | ||||
| }; | ||||
| 
 | ||||
| struct Tag { | ||||
|     znet_tapesoftware_dwl_wm_monitor_v1_tag_state state; | ||||
|     int numClients; | ||||
|     int focusedClient; | ||||
|     BarComponent component; | ||||
| 	znet_tapesoftware_dwl_wm_monitor_v1_tag_state state; | ||||
| 	int numClients; | ||||
| 	int focusedClient; | ||||
| 	BarComponent component; | ||||
| }; | ||||
| 
 | ||||
| struct Monitor; | ||||
| class Bar { | ||||
|     static const zwlr_layer_surface_v1_listener _layerSurfaceListener; | ||||
|     static const wl_callback_listener _frameListener; | ||||
| 	static const zwlr_layer_surface_v1_listener _layerSurfaceListener; | ||||
| 	static const wl_callback_listener _frameListener; | ||||
| 
 | ||||
|     wl_unique_ptr<wl_surface> _surface; | ||||
|     wl_unique_ptr<zwlr_layer_surface_v1> _layerSurface; | ||||
|     wl_unique_ptr<PangoContext> _pangoContext; | ||||
|     Monitor* _mon; | ||||
|     std::optional<ShmBuffer> _bufs; | ||||
|     std::vector<Tag> _tags; | ||||
|     BarComponent _layoutCmp, _titleCmp, _statusCmp; | ||||
|     bool _selected; | ||||
|     bool _invalid {false}; | ||||
| 	wl_unique_ptr<wl_surface> _surface; | ||||
| 	wl_unique_ptr<zwlr_layer_surface_v1> _layerSurface; | ||||
| 	wl_unique_ptr<PangoContext> _pangoContext; | ||||
| 	Monitor* _mon; | ||||
| 	std::optional<ShmBuffer> _bufs; | ||||
| 	std::vector<Tag> _tags; | ||||
| 	BarComponent _layoutCmp, _titleCmp, _statusCmp; | ||||
| 	bool _selected; | ||||
| 	bool _invalid {false}; | ||||
| 
 | ||||
|     // only vaild during render()
 | ||||
|     cairo_t* _painter {nullptr}; | ||||
|     int _x; | ||||
|     ColorScheme _colorScheme; | ||||
| 	// only vaild during render()
 | ||||
| 	cairo_t* _painter {nullptr}; | ||||
| 	int _x; | ||||
| 	ColorScheme _colorScheme; | ||||
| 
 | ||||
|     void layerSurfaceConfigure(uint32_t serial, uint32_t width, uint32_t height); | ||||
|     void render(); | ||||
|     void renderTags(); | ||||
|     void renderStatus(); | ||||
| 	void layerSurfaceConfigure(uint32_t serial, uint32_t width, uint32_t height); | ||||
| 	void render(); | ||||
| 	void renderTags(); | ||||
| 	void renderStatus(); | ||||
| 
 | ||||
|     // low-level rendering
 | ||||
|     void setColorScheme(const ColorScheme& scheme, bool invert = false); | ||||
|     void beginFg(); | ||||
|     void beginBg(); | ||||
|     void renderComponent(BarComponent& component); | ||||
|     BarComponent createComponent(const std::string& initial = {}); | ||||
| 	// low-level rendering
 | ||||
| 	void setColorScheme(const ColorScheme& scheme, bool invert = false); | ||||
| 	void beginFg(); | ||||
| 	void beginBg(); | ||||
| 	void renderComponent(BarComponent& component); | ||||
| 	BarComponent createComponent(const std::string& initial = {}); | ||||
| public: | ||||
|     Bar(Monitor *mon); | ||||
|     const wl_surface* surface() const; | ||||
|     bool visible() const; | ||||
|     void show(wl_output* output); | ||||
|     void hide(); | ||||
|     void setTag(int tag, znet_tapesoftware_dwl_wm_monitor_v1_tag_state state, int numClients, int focusedClient); | ||||
|     void setSelected(bool selected); | ||||
|     void setLayout(int layout); | ||||
|     void setTitle(const std::string& title); | ||||
|     void setStatus(const std::string& status); | ||||
|     void invalidate(); | ||||
|     void click(int x, int y, int btn); | ||||
| 	Bar(Monitor *mon); | ||||
| 	const wl_surface* surface() const; | ||||
| 	bool visible() const; | ||||
| 	void show(wl_output* output); | ||||
| 	void hide(); | ||||
| 	void setTag(int tag, znet_tapesoftware_dwl_wm_monitor_v1_tag_state state, int numClients, int focusedClient); | ||||
| 	void setSelected(bool selected); | ||||
| 	void setLayout(int layout); | ||||
| 	void setTitle(const std::string& title); | ||||
| 	void setStatus(const std::string& status); | ||||
| 	void invalidate(); | ||||
| 	void click(int x, int y, int btn); | ||||
| }; | ||||
|  | ||||
| @ -13,12 +13,12 @@ | ||||
| #include "net-tapesoftware-dwl-wm-unstable-v1-client-protocol.h" | ||||
| 
 | ||||
| struct Color { | ||||
|     Color() {} | ||||
|     constexpr Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a=255) : r(r), g(g), b(b), a(a) { } | ||||
|     uint8_t r, g, b, a {255}; | ||||
| 	Color() {} | ||||
| 	constexpr Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a=255) : r(r), g(g), b(b), a(a) { } | ||||
| 	uint8_t r, g, b, a {255}; | ||||
| }; | ||||
| struct ColorScheme { | ||||
|     Color fg, bg; | ||||
| 	Color fg, bg; | ||||
| }; | ||||
| union Arg { | ||||
| 	unsigned int ui; | ||||
| @ -53,8 +53,8 @@ void spawn(Monitor&, const Arg& arg); | ||||
| template<typename T> | ||||
| struct wl_deleter; | ||||
| #define WL_DELETER(type, fn) template<> struct wl_deleter<type> { \ | ||||
|     void operator()(type* v) { if(v) fn(v); } \ | ||||
|     } | ||||
| 	void operator()(type* v) { if(v) fn(v); } \ | ||||
| 	} | ||||
| 
 | ||||
| template<typename T> | ||||
| using wl_unique_ptr = std::unique_ptr<T, wl_deleter<T>>; | ||||
|  | ||||
| @ -17,10 +17,10 @@ constexpr ColorScheme colorActive = {Color(0xee, 0xee, 0xee), Color(0x00, 0x55, | ||||
| constexpr const char* termcmd[] = {"foot", nullptr}; | ||||
| 
 | ||||
| constexpr Button buttons[] = { | ||||
|     { ClkTagBar,       BTN_LEFT,   view,       {0} }, | ||||
|     { ClkTagBar,       BTN_RIGHT,  tag,        {0} }, | ||||
|     { ClkTagBar,       BTN_MIDDLE, toggletag,  {0} }, | ||||
|     { ClkLayoutSymbol, BTN_LEFT,   setlayout,  {.ui = 0} }, | ||||
|     { ClkLayoutSymbol, BTN_RIGHT,  setlayout,  {.ui = 2} }, | ||||
|     { ClkStatusText,   BTN_RIGHT,  spawn,      {.v = termcmd} }, | ||||
| 	{ ClkTagBar,       BTN_LEFT,   view,       {0} }, | ||||
| 	{ ClkTagBar,       BTN_RIGHT,  tag,        {0} }, | ||||
| 	{ ClkTagBar,       BTN_MIDDLE, toggletag,  {0} }, | ||||
| 	{ ClkLayoutSymbol, BTN_LEFT,   setlayout,  {.ui = 0} }, | ||||
| 	{ ClkLayoutSymbol, BTN_RIGHT,  setlayout,  {.ui = 2} }, | ||||
| 	{ ClkStatusText,   BTN_RIGHT,  spawn,      {.v = termcmd} }, | ||||
| }; | ||||
|  | ||||
| @ -9,63 +9,63 @@ | ||||
| // reads data from Reader, and passes complete lines to Consumer.
 | ||||
| template<size_t BufSize> | ||||
| class LineBuffer { | ||||
|     using Iterator = typename std::array<char, BufSize>::iterator; | ||||
|     std::array<char, BufSize> _buffer; | ||||
|     Iterator _bufferedTo; | ||||
|     Iterator _consumedTo; | ||||
|     bool _discardLine {false}; | ||||
| 	using Iterator = typename std::array<char, BufSize>::iterator; | ||||
| 	std::array<char, BufSize> _buffer; | ||||
| 	Iterator _bufferedTo; | ||||
| 	Iterator _consumedTo; | ||||
| 	bool _discardLine {false}; | ||||
| public: | ||||
|     LineBuffer() | ||||
|         : _bufferedTo {_buffer.begin()} | ||||
|         , _consumedTo {_buffer.begin()} | ||||
|     { | ||||
|     } | ||||
| 	LineBuffer() | ||||
| 		: _bufferedTo {_buffer.begin()} | ||||
| 		, _consumedTo {_buffer.begin()} | ||||
| 	{ | ||||
| 	} | ||||
| 
 | ||||
|     template<typename Reader, typename Consumer> | ||||
|     ssize_t readLines(const Reader& reader, const Consumer& consumer) | ||||
|     { | ||||
|         while (true) { | ||||
|             auto bytesRead = reader(_bufferedTo, _buffer.end() - _bufferedTo); | ||||
|             if (bytesRead <= 0) { | ||||
|                 return bytesRead; | ||||
|             } | ||||
|             _bufferedTo += bytesRead; | ||||
|             dispatchLines(consumer); | ||||
|             resetBuffer(); | ||||
|         } | ||||
|     } | ||||
| private: | ||||
|     template<typename Consumer> | ||||
|     void dispatchLines(const Consumer& consumer) | ||||
|     { | ||||
|         while (true) { | ||||
|             auto separator = std::find(_consumedTo, _bufferedTo, '\n'); | ||||
|             if (separator == _bufferedTo) { | ||||
|                 break; | ||||
|             } | ||||
|             size_t lineLength = separator - _consumedTo; | ||||
|             if (!_discardLine) { | ||||
|                 consumer(_consumedTo, lineLength); | ||||
|             } | ||||
|             _consumedTo = separator + 1; | ||||
|             _discardLine = false; | ||||
|         } | ||||
|     } | ||||
| 	template<typename Reader, typename Consumer> | ||||
| 	ssize_t readLines(const Reader& reader, const Consumer& consumer) | ||||
| 	{ | ||||
| 		while (true) { | ||||
| 			auto bytesRead = reader(_bufferedTo, _buffer.end() - _bufferedTo); | ||||
| 			if (bytesRead <= 0) { | ||||
| 				return bytesRead; | ||||
| 			} | ||||
| 			_bufferedTo += bytesRead; | ||||
| 			dispatchLines(consumer); | ||||
| 			resetBuffer(); | ||||
| 		} | ||||
| 	} | ||||
| 	private: | ||||
| 	template<typename Consumer> | ||||
| 	void dispatchLines(const Consumer& consumer) | ||||
| 	{ | ||||
| 		while (true) { | ||||
| 			auto separator = std::find(_consumedTo, _bufferedTo, '\n'); | ||||
| 			if (separator == _bufferedTo) { | ||||
| 				break; | ||||
| 			} | ||||
| 			size_t lineLength = separator - _consumedTo; | ||||
| 			if (!_discardLine) { | ||||
| 				consumer(_consumedTo, lineLength); | ||||
| 			} | ||||
| 			_consumedTo = separator + 1; | ||||
| 			_discardLine = false; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|     void resetBuffer() | ||||
|     { | ||||
|         size_t bytesRemaining = _bufferedTo - _consumedTo; | ||||
|         if (bytesRemaining == _buffer.size()) { | ||||
|             // line too long
 | ||||
|             _discardLine = true; | ||||
|             _consumedTo = _buffer.begin(); | ||||
|             _bufferedTo = _buffer.begin(); | ||||
|         } else if (bytesRemaining > 0 && _consumedTo > _buffer.begin()) { | ||||
|             // move the last partial message to the front of the buffer, so a full-sized
 | ||||
|             // message will fit
 | ||||
|             std::copy(_consumedTo, _bufferedTo, _buffer.begin()); | ||||
|             _consumedTo = _buffer.begin(); | ||||
|             _bufferedTo = _consumedTo + bytesRemaining; | ||||
|         } | ||||
|     } | ||||
| 	void resetBuffer() | ||||
| 	{ | ||||
| 		size_t bytesRemaining = _bufferedTo - _consumedTo; | ||||
| 		if (bytesRemaining == _buffer.size()) { | ||||
| 			// line too long
 | ||||
| 			_discardLine = true; | ||||
| 			_consumedTo = _buffer.begin(); | ||||
| 			_bufferedTo = _buffer.begin(); | ||||
| 		} else if (bytesRemaining > 0 && _consumedTo > _buffer.begin()) { | ||||
| 			// move the last partial message to the front of the buffer, so a full-sized
 | ||||
| 			// message will fit
 | ||||
| 			std::copy(_consumedTo, _bufferedTo, _buffer.begin()); | ||||
| 			_consumedTo = _buffer.begin(); | ||||
| 			_bufferedTo = _consumedTo + bytesRemaining; | ||||
| 		} | ||||
| 	} | ||||
| }; | ||||
|  | ||||
							
								
								
									
										778
									
								
								src/main.cpp
									
									
									
									
									
								
							
							
						
						
									
										778
									
								
								src/main.cpp
									
									
									
									
									
								
							| @ -26,26 +26,26 @@ | ||||
| #include "line_buffer.hpp" | ||||
| 
 | ||||
| struct Monitor { | ||||
|     uint32_t registryName; | ||||
|     std::string xdgName; | ||||
|     wl_unique_ptr<wl_output> wlOutput; | ||||
|     wl_unique_ptr<znet_tapesoftware_dwl_wm_monitor_v1> dwlMonitor; | ||||
|     std::optional<Bar> bar; | ||||
|     bool desiredVisibility {true}; | ||||
|     bool hasData; | ||||
|     uint32_t tags; | ||||
| 	uint32_t registryName; | ||||
| 	std::string xdgName; | ||||
| 	wl_unique_ptr<wl_output> wlOutput; | ||||
| 	wl_unique_ptr<znet_tapesoftware_dwl_wm_monitor_v1> dwlMonitor; | ||||
| 	std::optional<Bar> bar; | ||||
| 	bool desiredVisibility {true}; | ||||
| 	bool hasData; | ||||
| 	uint32_t tags; | ||||
| }; | ||||
| 
 | ||||
| struct SeatPointer { | ||||
|     wl_unique_ptr<wl_pointer> wlPointer; | ||||
|     Bar* focusedBar; | ||||
|     int x, y; | ||||
|     std::vector<int> btns; | ||||
| 	wl_unique_ptr<wl_pointer> wlPointer; | ||||
| 	Bar* focusedBar; | ||||
| 	int x, y; | ||||
| 	std::vector<int> btns; | ||||
| }; | ||||
| struct Seat { | ||||
|     uint32_t name; | ||||
|     wl_unique_ptr<wl_seat> wlSeat; | ||||
|     std::optional<SeatPointer> pointer; | ||||
| 	uint32_t name; | ||||
| 	wl_unique_ptr<wl_seat> wlSeat; | ||||
| 	std::optional<SeatPointer> pointer; | ||||
| }; | ||||
| 
 | ||||
| static void updatemon(Monitor &mon); | ||||
| @ -81,238 +81,238 @@ static bool quitting {false}; | ||||
| 
 | ||||
| void view(Monitor& m, const Arg& arg) | ||||
| { | ||||
|     znet_tapesoftware_dwl_wm_monitor_v1_set_tags(m.dwlMonitor.get(), arg.ui, 1); | ||||
| 	znet_tapesoftware_dwl_wm_monitor_v1_set_tags(m.dwlMonitor.get(), arg.ui, 1); | ||||
| } | ||||
| void toggleview(Monitor& m, const Arg& arg) | ||||
| { | ||||
|     znet_tapesoftware_dwl_wm_monitor_v1_set_tags(m.dwlMonitor.get(), m.tags ^ arg.ui, 0); | ||||
| 	znet_tapesoftware_dwl_wm_monitor_v1_set_tags(m.dwlMonitor.get(), m.tags ^ arg.ui, 0); | ||||
| } | ||||
| void setlayout(Monitor& m, const Arg& arg) | ||||
| { | ||||
|     znet_tapesoftware_dwl_wm_monitor_v1_set_layout(m.dwlMonitor.get(), arg.ui); | ||||
| 	znet_tapesoftware_dwl_wm_monitor_v1_set_layout(m.dwlMonitor.get(), arg.ui); | ||||
| } | ||||
| void tag(Monitor& m, const Arg& arg) | ||||
| { | ||||
|     znet_tapesoftware_dwl_wm_monitor_v1_set_client_tags(m.dwlMonitor.get(), 0, arg.ui); | ||||
| 	znet_tapesoftware_dwl_wm_monitor_v1_set_client_tags(m.dwlMonitor.get(), 0, arg.ui); | ||||
| } | ||||
| void toggletag(Monitor& m, const Arg& arg) | ||||
| { | ||||
|     znet_tapesoftware_dwl_wm_monitor_v1_set_client_tags(m.dwlMonitor.get(), 0xffffff, arg.ui); | ||||
| 	znet_tapesoftware_dwl_wm_monitor_v1_set_client_tags(m.dwlMonitor.get(), 0xffffff, arg.ui); | ||||
| } | ||||
| void spawn(Monitor&, const Arg& arg) | ||||
| { | ||||
|     if (fork() == 0) { | ||||
|         auto argv = static_cast<char* const*>(arg.v); | ||||
|         setsid(); | ||||
|         execvp(argv[0], argv); | ||||
|         fprintf(stderr, "somebar: execvp %s ", argv[0]); | ||||
|         perror(" failed"); | ||||
|         exit(1); | ||||
|     } | ||||
| 	if (fork() == 0) { | ||||
| 		auto argv = static_cast<char* const*>(arg.v); | ||||
| 		setsid(); | ||||
| 		execvp(argv[0], argv); | ||||
| 		fprintf(stderr, "somebar: execvp %s ", argv[0]); | ||||
| 		perror(" failed"); | ||||
| 		exit(1); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static const struct xdg_wm_base_listener xdgWmBaseListener = { | ||||
|     [](void*, xdg_wm_base* sender, uint32_t serial) { | ||||
|         xdg_wm_base_pong(sender, serial); | ||||
|     } | ||||
| 	[](void*, xdg_wm_base* sender, uint32_t serial) { | ||||
| 		xdg_wm_base_pong(sender, serial); | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| static const struct zxdg_output_v1_listener xdgOutputListener = { | ||||
|     .logical_position = [](void*, zxdg_output_v1*, int, int) { }, | ||||
|     .logical_size = [](void*, zxdg_output_v1*, int, int) { }, | ||||
|     .done = [](void*, zxdg_output_v1*) { }, | ||||
|     .name = [](void* mp, zxdg_output_v1* xdgOutput, const char* name) { | ||||
|         auto& monitor = *static_cast<Monitor*>(mp); | ||||
|         monitor.xdgName = name; | ||||
|         zxdg_output_v1_destroy(xdgOutput); | ||||
|     }, | ||||
|     .description = [](void*, zxdg_output_v1*, const char*) { }, | ||||
| 	.logical_position = [](void*, zxdg_output_v1*, int, int) { }, | ||||
| 	.logical_size = [](void*, zxdg_output_v1*, int, int) { }, | ||||
| 	.done = [](void*, zxdg_output_v1*) { }, | ||||
| 	.name = [](void* mp, zxdg_output_v1* xdgOutput, const char* name) { | ||||
| 		auto& monitor = *static_cast<Monitor*>(mp); | ||||
| 		monitor.xdgName = name; | ||||
| 		zxdg_output_v1_destroy(xdgOutput); | ||||
| 	}, | ||||
| 	.description = [](void*, zxdg_output_v1*, const char*) { }, | ||||
| }; | ||||
| 
 | ||||
| static Bar* barFromSurface(const wl_surface *surface) | ||||
| { | ||||
|     auto mon = std::find_if(begin(monitors), end(monitors), [surface](const Monitor& mon) { | ||||
|         return mon.bar && mon.bar->surface() == surface; | ||||
|     }); | ||||
|     return mon != end(monitors) && mon->bar ? &*mon->bar : nullptr; | ||||
| 	auto mon = std::find_if(begin(monitors), end(monitors), [surface](const Monitor& mon) { | ||||
| 		return mon.bar && mon.bar->surface() == surface; | ||||
| 	}); | ||||
| 	return mon != end(monitors) && mon->bar ? &*mon->bar : nullptr; | ||||
| } | ||||
| static const struct wl_pointer_listener pointerListener = { | ||||
|     .enter = [](void* sp, wl_pointer* pointer, uint32_t serial, | ||||
|                 wl_surface* surface, wl_fixed_t x, wl_fixed_t y) | ||||
|     { | ||||
|         auto& seat = *static_cast<Seat*>(sp); | ||||
|         seat.pointer->focusedBar = barFromSurface(surface); | ||||
|         if (!cursorImage) { | ||||
|             auto cursorTheme = wl_cursor_theme_load(nullptr, 24, shm); | ||||
|             cursorImage = wl_cursor_theme_get_cursor(cursorTheme, "left_ptr")->images[0]; | ||||
|             cursorSurface = wl_compositor_create_surface(compositor); | ||||
|             wl_surface_attach(cursorSurface, wl_cursor_image_get_buffer(cursorImage), 0, 0); | ||||
|             wl_surface_commit(cursorSurface); | ||||
|         } | ||||
|         wl_pointer_set_cursor(pointer, serial, cursorSurface, | ||||
|             cursorImage->hotspot_x, cursorImage->hotspot_y); | ||||
|     }, | ||||
|     .leave = [](void* sp, wl_pointer*, uint32_t serial, wl_surface*) { | ||||
|         auto& seat = *static_cast<Seat*>(sp); | ||||
|         seat.pointer->focusedBar = nullptr; | ||||
|     }, | ||||
|     .motion = [](void* sp, wl_pointer*, uint32_t, wl_fixed_t x, wl_fixed_t y) { | ||||
|         auto& seat = *static_cast<Seat*>(sp); | ||||
|         seat.pointer->x = wl_fixed_to_int(x); | ||||
|         seat.pointer->y = wl_fixed_to_int(y); | ||||
|     }, | ||||
|     .button = [](void* sp, wl_pointer*, uint32_t, uint32_t, uint32_t button, uint32_t pressed) { | ||||
|         auto& seat = *static_cast<Seat*>(sp); | ||||
|         auto it = std::find(begin(seat.pointer->btns), end(seat.pointer->btns), button); | ||||
|         if (pressed == WL_POINTER_BUTTON_STATE_PRESSED && it == end(seat.pointer->btns)) { | ||||
|             seat.pointer->btns.push_back(button); | ||||
|         } else if (pressed == WL_POINTER_BUTTON_STATE_RELEASED && it != end(seat.pointer->btns)) { | ||||
|             seat.pointer->btns.erase(it); | ||||
|         } | ||||
|     }, | ||||
|     .axis = [](void* sp, wl_pointer*, uint32_t, uint32_t, wl_fixed_t) { }, | ||||
|     .frame = [](void* sp, wl_pointer*) { | ||||
|         auto& seat = *static_cast<Seat*>(sp); | ||||
|         if (!seat.pointer->focusedBar) return; | ||||
|         for (auto btn : seat.pointer->btns) { | ||||
|             seat.pointer->focusedBar->click(seat.pointer->x, seat.pointer->y, btn); | ||||
|         } | ||||
|         seat.pointer->btns.clear(); | ||||
|     }, | ||||
|     .axis_source = [](void*, wl_pointer*, uint32_t) { }, | ||||
|     .axis_stop = [](void*, wl_pointer*, uint32_t, uint32_t) { }, | ||||
|     .axis_discrete = [](void*, wl_pointer*, uint32_t, int32_t) { }, | ||||
| 	.enter = [](void* sp, wl_pointer* pointer, uint32_t serial, | ||||
| 	wl_surface* surface, wl_fixed_t x, wl_fixed_t y) | ||||
| 	{ | ||||
| 		auto& seat = *static_cast<Seat*>(sp); | ||||
| 		seat.pointer->focusedBar = barFromSurface(surface); | ||||
| 		if (!cursorImage) { | ||||
| 			auto cursorTheme = wl_cursor_theme_load(nullptr, 24, shm); | ||||
| 			cursorImage = wl_cursor_theme_get_cursor(cursorTheme, "left_ptr")->images[0]; | ||||
| 			cursorSurface = wl_compositor_create_surface(compositor); | ||||
| 			wl_surface_attach(cursorSurface, wl_cursor_image_get_buffer(cursorImage), 0, 0); | ||||
| 			wl_surface_commit(cursorSurface); | ||||
| 		} | ||||
| 		wl_pointer_set_cursor(pointer, serial, cursorSurface, | ||||
| 		cursorImage->hotspot_x, cursorImage->hotspot_y); | ||||
| 	}, | ||||
| 	.leave = [](void* sp, wl_pointer*, uint32_t serial, wl_surface*) { | ||||
| 		auto& seat = *static_cast<Seat*>(sp); | ||||
| 		seat.pointer->focusedBar = nullptr; | ||||
| 	}, | ||||
| 	.motion = [](void* sp, wl_pointer*, uint32_t, wl_fixed_t x, wl_fixed_t y) { | ||||
| 		auto& seat = *static_cast<Seat*>(sp); | ||||
| 		seat.pointer->x = wl_fixed_to_int(x); | ||||
| 		seat.pointer->y = wl_fixed_to_int(y); | ||||
| 	}, | ||||
| 	.button = [](void* sp, wl_pointer*, uint32_t, uint32_t, uint32_t button, uint32_t pressed) { | ||||
| 		auto& seat = *static_cast<Seat*>(sp); | ||||
| 		auto it = std::find(begin(seat.pointer->btns), end(seat.pointer->btns), button); | ||||
| 		if (pressed == WL_POINTER_BUTTON_STATE_PRESSED && it == end(seat.pointer->btns)) { | ||||
| 			seat.pointer->btns.push_back(button); | ||||
| 		} else if (pressed == WL_POINTER_BUTTON_STATE_RELEASED && it != end(seat.pointer->btns)) { | ||||
| 			seat.pointer->btns.erase(it); | ||||
| 		} | ||||
| 	}, | ||||
| 	.axis = [](void* sp, wl_pointer*, uint32_t, uint32_t, wl_fixed_t) { }, | ||||
| 	.frame = [](void* sp, wl_pointer*) { | ||||
| 		auto& seat = *static_cast<Seat*>(sp); | ||||
| 		if (!seat.pointer->focusedBar) return; | ||||
| 		for (auto btn : seat.pointer->btns) { | ||||
| 			seat.pointer->focusedBar->click(seat.pointer->x, seat.pointer->y, btn); | ||||
| 		} | ||||
| 		seat.pointer->btns.clear(); | ||||
| 	}, | ||||
| 	.axis_source = [](void*, wl_pointer*, uint32_t) { }, | ||||
| 	.axis_stop = [](void*, wl_pointer*, uint32_t, uint32_t) { }, | ||||
| 	.axis_discrete = [](void*, wl_pointer*, uint32_t, int32_t) { }, | ||||
| }; | ||||
| 
 | ||||
| static const struct wl_seat_listener seatListener = { | ||||
|     .capabilities = [](void* sp, wl_seat*, uint32_t cap) | ||||
|     { | ||||
|         auto& seat = *static_cast<Seat*>(sp); | ||||
|         auto hasPointer = cap & WL_SEAT_CAPABILITY_POINTER; | ||||
|         if (!seat.pointer && hasPointer) { | ||||
|             auto &pointer = seat.pointer.emplace(); | ||||
|             pointer.wlPointer = wl_unique_ptr<wl_pointer> {wl_seat_get_pointer(seat.wlSeat.get())}; | ||||
|             wl_pointer_add_listener(seat.pointer->wlPointer.get(), &pointerListener, &seat); | ||||
|         } else if (seat.pointer && !hasPointer) { | ||||
|             seat.pointer.reset(); | ||||
|         } | ||||
|     }, | ||||
|     .name = [](void*, wl_seat*, const char *name) { } | ||||
| 	.capabilities = [](void* sp, wl_seat*, uint32_t cap) | ||||
| 	{ | ||||
| 		auto& seat = *static_cast<Seat*>(sp); | ||||
| 		auto hasPointer = cap & WL_SEAT_CAPABILITY_POINTER; | ||||
| 		if (!seat.pointer && hasPointer) { | ||||
| 			auto &pointer = seat.pointer.emplace(); | ||||
| 			pointer.wlPointer = wl_unique_ptr<wl_pointer> {wl_seat_get_pointer(seat.wlSeat.get())}; | ||||
| 			wl_pointer_add_listener(seat.pointer->wlPointer.get(), &pointerListener, &seat); | ||||
| 		} else if (seat.pointer && !hasPointer) { | ||||
| 			seat.pointer.reset(); | ||||
| 		} | ||||
| 	}, | ||||
| 	.name = [](void*, wl_seat*, const char *name) { } | ||||
| }; | ||||
| 
 | ||||
| static const struct znet_tapesoftware_dwl_wm_v1_listener dwlWmListener = { | ||||
|     .tag = [](void*, znet_tapesoftware_dwl_wm_v1*, const char* name) { | ||||
|         tagNames.push_back(name); | ||||
|     }, | ||||
|     .layout = [](void*, znet_tapesoftware_dwl_wm_v1*, const char* name) { | ||||
|         layoutNames.push_back(name); | ||||
|     }, | ||||
| 	.tag = [](void*, znet_tapesoftware_dwl_wm_v1*, const char* name) { | ||||
| 		tagNames.push_back(name); | ||||
| 	}, | ||||
| 	.layout = [](void*, znet_tapesoftware_dwl_wm_v1*, const char* name) { | ||||
| 		layoutNames.push_back(name); | ||||
| 	}, | ||||
| }; | ||||
| 
 | ||||
| static const struct znet_tapesoftware_dwl_wm_monitor_v1_listener dwlWmMonitorListener { | ||||
|     .selected = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*, uint32_t selected) { | ||||
|         auto mon = static_cast<Monitor*>(mv); | ||||
|         if (selected) { | ||||
|             selmon = mon; | ||||
|         } else if (selmon == mon) { | ||||
|             selmon = nullptr; | ||||
|         } | ||||
|         mon->bar->setSelected(selected); | ||||
|     }, | ||||
|     .tag = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*, uint32_t tag, uint32_t state, uint32_t numClients, int32_t focusedClient) { | ||||
|         auto mon = static_cast<Monitor*>(mv); | ||||
|         mon->bar->setTag(tag, static_cast<znet_tapesoftware_dwl_wm_monitor_v1_tag_state>(state), numClients, focusedClient); | ||||
|         uint32_t mask = 1 << tag; | ||||
|         if (state & ZNET_TAPESOFTWARE_DWL_WM_MONITOR_V1_TAG_STATE_ACTIVE) { | ||||
|             mon->tags |= mask; | ||||
|         } else { | ||||
|             mon->tags &= ~mask; | ||||
|         } | ||||
|     }, | ||||
|     .layout = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*, uint32_t layout) { | ||||
|         auto mon = static_cast<Monitor*>(mv); | ||||
|         mon->bar->setLayout(layout); | ||||
|     }, | ||||
|     .title = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*, const char* title) { | ||||
|         auto mon = static_cast<Monitor*>(mv); | ||||
|         mon->bar->setTitle(title); | ||||
|     }, | ||||
|     .frame = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*) { | ||||
|         auto mon = static_cast<Monitor*>(mv); | ||||
|         mon->hasData = true; | ||||
|         updatemon(*mon); | ||||
|     } | ||||
| 	.selected = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*, uint32_t selected) { | ||||
| 		auto mon = static_cast<Monitor*>(mv); | ||||
| 		if (selected) { | ||||
| 			selmon = mon; | ||||
| 		} else if (selmon == mon) { | ||||
| 			selmon = nullptr; | ||||
| 		} | ||||
| 		mon->bar->setSelected(selected); | ||||
| 	}, | ||||
| 	.tag = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*, uint32_t tag, uint32_t state, uint32_t numClients, int32_t focusedClient) { | ||||
| 		auto mon = static_cast<Monitor*>(mv); | ||||
| 		mon->bar->setTag(tag, static_cast<znet_tapesoftware_dwl_wm_monitor_v1_tag_state>(state), numClients, focusedClient); | ||||
| 		uint32_t mask = 1 << tag; | ||||
| 		if (state & ZNET_TAPESOFTWARE_DWL_WM_MONITOR_V1_TAG_STATE_ACTIVE) { | ||||
| 			mon->tags |= mask; | ||||
| 		} else { | ||||
| 			mon->tags &= ~mask; | ||||
| 		} | ||||
| 	}, | ||||
| 	.layout = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*, uint32_t layout) { | ||||
| 		auto mon = static_cast<Monitor*>(mv); | ||||
| 		mon->bar->setLayout(layout); | ||||
| 	}, | ||||
| 	.title = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*, const char* title) { | ||||
| 		auto mon = static_cast<Monitor*>(mv); | ||||
| 		mon->bar->setTitle(title); | ||||
| 	}, | ||||
| 	.frame = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*) { | ||||
| 		auto mon = static_cast<Monitor*>(mv); | ||||
| 		mon->hasData = true; | ||||
| 		updatemon(*mon); | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| static void setupMonitor(Monitor& monitor) { | ||||
|     monitor.dwlMonitor.reset(znet_tapesoftware_dwl_wm_v1_get_monitor(dwlWm, monitor.wlOutput.get())); | ||||
|     monitor.bar.emplace(&monitor); | ||||
|     monitor.bar->setStatus(lastStatus); | ||||
|     auto xdgOutput = zxdg_output_manager_v1_get_xdg_output(xdgOutputManager, monitor.wlOutput.get()); | ||||
|     zxdg_output_v1_add_listener(xdgOutput, &xdgOutputListener, &monitor); | ||||
|     znet_tapesoftware_dwl_wm_monitor_v1_add_listener(monitor.dwlMonitor.get(), &dwlWmMonitorListener, &monitor); | ||||
| 	monitor.dwlMonitor.reset(znet_tapesoftware_dwl_wm_v1_get_monitor(dwlWm, monitor.wlOutput.get())); | ||||
| 	monitor.bar.emplace(&monitor); | ||||
| 	monitor.bar->setStatus(lastStatus); | ||||
| 	auto xdgOutput = zxdg_output_manager_v1_get_xdg_output(xdgOutputManager, monitor.wlOutput.get()); | ||||
| 	zxdg_output_v1_add_listener(xdgOutput, &xdgOutputListener, &monitor); | ||||
| 	znet_tapesoftware_dwl_wm_monitor_v1_add_listener(monitor.dwlMonitor.get(), &dwlWmMonitorListener, &monitor); | ||||
| } | ||||
| 
 | ||||
| static void updatemon(Monitor& mon) | ||||
| { | ||||
|     if (!mon.hasData) return; | ||||
|     if (mon.desiredVisibility) { | ||||
|         if (mon.bar->visible()) { | ||||
|             mon.bar->invalidate(); | ||||
|         } else { | ||||
|             mon.bar->show(mon.wlOutput.get()); | ||||
|         } | ||||
|     } else if (mon.bar->visible()) { | ||||
|         mon.bar->hide(); | ||||
|     } | ||||
| 	if (!mon.hasData) return; | ||||
| 	if (mon.desiredVisibility) { | ||||
| 		if (mon.bar->visible()) { | ||||
| 			mon.bar->invalidate(); | ||||
| 		} else { | ||||
| 			mon.bar->show(mon.wlOutput.get()); | ||||
| 		} | ||||
| 	} else if (mon.bar->visible()) { | ||||
| 		mon.bar->hide(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // called after we have received the initial batch of globals
 | ||||
| static void onReady() | ||||
| { | ||||
|     requireGlobal(compositor, "wl_compositor"); | ||||
|     requireGlobal(shm, "wl_shm"); | ||||
|     requireGlobal(wlrLayerShell, "zwlr_layer_shell_v1"); | ||||
|     requireGlobal(xdgOutputManager, "zxdg_output_manager_v1"); | ||||
|     requireGlobal(dwlWm, "znet_tapesoftware_dwl_wm_v1"); | ||||
|     setupStatusFifo(); | ||||
|     wl_display_roundtrip(display); // roundtrip so we receive all dwl tags etc.
 | ||||
|     ready = true; | ||||
|     for (auto& monitor : monitors) { | ||||
|         setupMonitor(monitor); | ||||
|     } | ||||
| 	requireGlobal(compositor, "wl_compositor"); | ||||
| 	requireGlobal(shm, "wl_shm"); | ||||
| 	requireGlobal(wlrLayerShell, "zwlr_layer_shell_v1"); | ||||
| 	requireGlobal(xdgOutputManager, "zxdg_output_manager_v1"); | ||||
| 	requireGlobal(dwlWm, "znet_tapesoftware_dwl_wm_v1"); | ||||
| 	setupStatusFifo(); | ||||
| 	wl_display_roundtrip(display); // roundtrip so we receive all dwl tags etc.
 | ||||
| 	ready = true; | ||||
| 	for (auto& monitor : monitors) { | ||||
| 		setupMonitor(monitor); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void setupStatusFifo() | ||||
| { | ||||
|     for (auto i=0; i<100; i++) { | ||||
|         auto path = std::string{getenv("XDG_RUNTIME_DIR")} + "/somebar-" + std::to_string(i); | ||||
|         auto result = mkfifo(path.c_str(), 0666); | ||||
|         if (result == 0) { | ||||
|             auto fd = open(path.c_str(), O_CLOEXEC | O_NONBLOCK | O_RDONLY); | ||||
|             if (fd < 0) { | ||||
|                 diesys("open status fifo reader"); | ||||
|             } | ||||
|             statusFifoName = path; | ||||
|             statusFifoFd = fd; | ||||
| 	for (auto i=0; i<100; i++) { | ||||
| 		auto path = std::string{getenv("XDG_RUNTIME_DIR")} + "/somebar-" + std::to_string(i); | ||||
| 		auto result = mkfifo(path.c_str(), 0666); | ||||
| 		if (result == 0) { | ||||
| 			auto fd = open(path.c_str(), O_CLOEXEC | O_NONBLOCK | O_RDONLY); | ||||
| 			if (fd < 0) { | ||||
| 				diesys("open status fifo reader"); | ||||
| 			} | ||||
| 			statusFifoName = path; | ||||
| 			statusFifoFd = fd; | ||||
| 
 | ||||
|             fd = open(path.c_str(), O_CLOEXEC | O_WRONLY); | ||||
|             if (fd < 0) { | ||||
|                 diesys("open status fifo writer"); | ||||
|             } | ||||
|             statusFifoWriter = fd; | ||||
| 			fd = open(path.c_str(), O_CLOEXEC | O_WRONLY); | ||||
| 			if (fd < 0) { | ||||
| 				diesys("open status fifo writer"); | ||||
| 			} | ||||
| 			statusFifoWriter = fd; | ||||
| 
 | ||||
|             epoll_event ev = {0}; | ||||
|             ev.events = EPOLLIN; | ||||
|             ev.data.fd = statusFifoFd; | ||||
|             if (epoll_ctl(epoll, EPOLL_CTL_ADD, statusFifoFd, &ev) < 0) { | ||||
|                 diesys("epoll_ctl add status fifo"); | ||||
|             } | ||||
|             return; | ||||
|         } else if (errno != EEXIST) { | ||||
|             diesys("mkfifo"); | ||||
|         } | ||||
|     } | ||||
| 			epoll_event ev = {0}; | ||||
| 			ev.events = EPOLLIN; | ||||
| 			ev.data.fd = statusFifoFd; | ||||
| 			if (epoll_ctl(epoll, EPOLL_CTL_ADD, statusFifoFd, &ev) < 0) { | ||||
| 				diesys("epoll_ctl add status fifo"); | ||||
| 			} | ||||
| 			return; | ||||
| 		} else if (errno != EEXIST) { | ||||
| 			diesys("mkfifo"); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| const std::string prefixStatus = "status "; | ||||
| @ -325,243 +325,243 @@ const std::string argSelected = "selected"; | ||||
| template<typename T> | ||||
| static void updateVisibility(const std::string& name, T updater) | ||||
| { | ||||
|     auto isCurrent = name == argSelected; | ||||
|     auto isAll = name == argAll; | ||||
|     for (auto& mon : monitors) { | ||||
|         if (isAll || | ||||
|             isCurrent && &mon == selmon || | ||||
|             mon.xdgName == name) { | ||||
|             auto newVisibility = updater(mon.desiredVisibility); | ||||
|             if (newVisibility != mon.desiredVisibility) { | ||||
|                 mon.desiredVisibility = newVisibility; | ||||
|                 updatemon(mon); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 	auto isCurrent = name == argSelected; | ||||
| 	auto isAll = name == argAll; | ||||
| 	for (auto& mon : monitors) { | ||||
| 		if (isAll || | ||||
| 			isCurrent && &mon == selmon || | ||||
| 			mon.xdgName == name) { | ||||
| 			auto newVisibility = updater(mon.desiredVisibility); | ||||
| 			if (newVisibility != mon.desiredVisibility) { | ||||
| 				mon.desiredVisibility = newVisibility; | ||||
| 				updatemon(mon); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static LineBuffer<512> _statusBuffer; | ||||
| static void onStatus() | ||||
| { | ||||
|     _statusBuffer.readLines( | ||||
|         [](void* p, size_t size) { | ||||
|             return read(statusFifoFd, p, size); | ||||
|         }, | ||||
|         [](const char* buffer, size_t n) { | ||||
|             auto str = std::string {buffer, n}; | ||||
|             if (str.rfind(prefixStatus, 0) == 0) { | ||||
|                 lastStatus = str.substr(prefixStatus.size()); | ||||
|                 for (auto &monitor : monitors) { | ||||
|                     if (monitor.bar) { | ||||
|                         monitor.bar->setStatus(lastStatus); | ||||
|                         monitor.bar->invalidate(); | ||||
|                     } | ||||
|                 } | ||||
|             } else if (str.rfind(prefixShow, 0) == 0) { | ||||
|                 updateVisibility(str.substr(prefixShow.size()), [](bool) { return true; }); | ||||
|             } else if (str.rfind(prefixHide, 0) == 0) { | ||||
|                 updateVisibility(str.substr(prefixHide.size()), [](bool) { return false; }); | ||||
|             } else if (str.rfind(prefixToggle, 0) == 0) { | ||||
|                 updateVisibility(str.substr(prefixToggle.size()), [](bool vis) { return !vis; }); | ||||
|             } | ||||
|         }); | ||||
| 	_statusBuffer.readLines( | ||||
| 	[](void* p, size_t size) { | ||||
| 		return read(statusFifoFd, p, size); | ||||
| 	}, | ||||
| 	[](const char* buffer, size_t n) { | ||||
| 		auto str = std::string {buffer, n}; | ||||
| 		if (str.rfind(prefixStatus, 0) == 0) { | ||||
| 			lastStatus = str.substr(prefixStatus.size()); | ||||
| 			for (auto &monitor : monitors) { | ||||
| 				if (monitor.bar) { | ||||
| 					monitor.bar->setStatus(lastStatus); | ||||
| 					monitor.bar->invalidate(); | ||||
| 				} | ||||
| 			} | ||||
| 		} else if (str.rfind(prefixShow, 0) == 0) { | ||||
| 			updateVisibility(str.substr(prefixShow.size()), [](bool) { return true; }); | ||||
| 		} else if (str.rfind(prefixHide, 0) == 0) { | ||||
| 			updateVisibility(str.substr(prefixHide.size()), [](bool) { return false; }); | ||||
| 		} else if (str.rfind(prefixToggle, 0) == 0) { | ||||
| 			updateVisibility(str.substr(prefixToggle.size()), [](bool vis) { return !vis; }); | ||||
| 		} | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| struct HandleGlobalHelper { | ||||
|     wl_registry* registry; | ||||
|     uint32_t name; | ||||
|     const char* interface; | ||||
| 	wl_registry* registry; | ||||
| 	uint32_t name; | ||||
| 	const char* interface; | ||||
| 
 | ||||
|     template<typename T> | ||||
|     bool handle(T& store, const wl_interface& iface, int version) { | ||||
|         if (strcmp(interface, iface.name)) return false; | ||||
|         store = static_cast<T>(wl_registry_bind(registry, name, &iface, version)); | ||||
|         return true; | ||||
|     } | ||||
| 	template<typename T> | ||||
| 	bool handle(T& store, const wl_interface& iface, int version) { | ||||
| 		if (strcmp(interface, iface.name)) return false; | ||||
| 		store = static_cast<T>(wl_registry_bind(registry, name, &iface, version)); | ||||
| 		return true; | ||||
| 	} | ||||
| }; | ||||
| static void registryHandleGlobal(void*, wl_registry* registry, uint32_t name, const char* interface, uint32_t version) | ||||
| { | ||||
|     auto reg = HandleGlobalHelper { registry, name, interface }; | ||||
|     if (reg.handle(compositor, wl_compositor_interface, 4)) return; | ||||
|     if (reg.handle(shm, wl_shm_interface, 1)) return; | ||||
|     if (reg.handle(wlrLayerShell, zwlr_layer_shell_v1_interface, 4)) return; | ||||
|     if (reg.handle(xdgOutputManager, zxdg_output_manager_v1_interface, 3)) return; | ||||
|     if (reg.handle(xdgWmBase, xdg_wm_base_interface, 2)) { | ||||
|         xdg_wm_base_add_listener(xdgWmBase, &xdgWmBaseListener, nullptr); | ||||
|         return; | ||||
|     } | ||||
|     if (reg.handle(dwlWm, znet_tapesoftware_dwl_wm_v1_interface, 1)) { | ||||
|         znet_tapesoftware_dwl_wm_v1_add_listener(dwlWm, &dwlWmListener, nullptr); | ||||
|         return; | ||||
|     } | ||||
|     if (wl_seat *wlSeat; reg.handle(wlSeat, wl_seat_interface, 7)) { | ||||
|         auto& seat = seats.emplace_back(Seat {name, wl_unique_ptr<wl_seat> {wlSeat}}); | ||||
|         wl_seat_add_listener(wlSeat, &seatListener, &seat); | ||||
|         return; | ||||
|     } | ||||
|     if (wl_output *output; reg.handle(output, wl_output_interface, 1)) { | ||||
|         auto& m = monitors.emplace_back(Monitor {name, {}, wl_unique_ptr<wl_output> {output}}); | ||||
|         if (ready) { | ||||
|             setupMonitor(m); | ||||
|         } | ||||
|         return; | ||||
|     } | ||||
| 	auto reg = HandleGlobalHelper { registry, name, interface }; | ||||
| 	if (reg.handle(compositor, wl_compositor_interface, 4)) return; | ||||
| 	if (reg.handle(shm, wl_shm_interface, 1)) return; | ||||
| 	if (reg.handle(wlrLayerShell, zwlr_layer_shell_v1_interface, 4)) return; | ||||
| 	if (reg.handle(xdgOutputManager, zxdg_output_manager_v1_interface, 3)) return; | ||||
| 	if (reg.handle(xdgWmBase, xdg_wm_base_interface, 2)) { | ||||
| 		xdg_wm_base_add_listener(xdgWmBase, &xdgWmBaseListener, nullptr); | ||||
| 		return; | ||||
| 	} | ||||
| 	if (reg.handle(dwlWm, znet_tapesoftware_dwl_wm_v1_interface, 1)) { | ||||
| 		znet_tapesoftware_dwl_wm_v1_add_listener(dwlWm, &dwlWmListener, nullptr); | ||||
| 		return; | ||||
| 	} | ||||
| 	if (wl_seat *wlSeat; reg.handle(wlSeat, wl_seat_interface, 7)) { | ||||
| 		auto& seat = seats.emplace_back(Seat {name, wl_unique_ptr<wl_seat> {wlSeat}}); | ||||
| 		wl_seat_add_listener(wlSeat, &seatListener, &seat); | ||||
| 		return; | ||||
| 	} | ||||
| 	if (wl_output *output; reg.handle(output, wl_output_interface, 1)) { | ||||
| 		auto& m = monitors.emplace_back(Monitor {name, {}, wl_unique_ptr<wl_output> {output}}); | ||||
| 		if (ready) { | ||||
| 			setupMonitor(m); | ||||
| 		} | ||||
| 		return; | ||||
| 	} | ||||
| } | ||||
| static void registryHandleRemove(void*, wl_registry* registry, uint32_t name) | ||||
| { | ||||
|     monitors.remove_if([name](const Monitor &mon) { return mon.registryName == name; }); | ||||
|     seats.remove_if([name](const Seat &seat) { return seat.name == name; }); | ||||
| 	monitors.remove_if([name](const Monitor &mon) { return mon.registryName == name; }); | ||||
| 	seats.remove_if([name](const Seat &seat) { return seat.name == name; }); | ||||
| } | ||||
| static const struct wl_registry_listener registry_listener = { | ||||
|     .global = registryHandleGlobal, | ||||
|     .global_remove = registryHandleRemove, | ||||
| 	.global = registryHandleGlobal, | ||||
| 	.global_remove = registryHandleRemove, | ||||
| }; | ||||
| 
 | ||||
| int main(int argc, char* argv[]) | ||||
| { | ||||
|     int opt; | ||||
|     while ((opt = getopt(argc, argv, "chv")) != -1) { | ||||
|         switch (opt) { | ||||
|             case 'h': | ||||
|                 printf("Usage: %s [-h] [-v] [-c command]\n", argv[0]); | ||||
|                 printf("  -h: Show this help\n"); | ||||
|                 printf("  -v: Show somebar version\n"); | ||||
|                 printf("  -c: Sends a command to sombar. See README for details.\n"); | ||||
|                 printf("If any of these are specified, somebar exits after the action.\n"); | ||||
|                 printf("Otherwise, somebar will display itself.\n"); | ||||
|                 exit(0); | ||||
|             case 'v': | ||||
|                 printf("somebar " SOMEBAR_VERSION "\n"); | ||||
|                 exit(0); | ||||
|             case 'c': | ||||
|                 if (optind >= argc) { | ||||
|                     die("Expected command"); | ||||
|                 } | ||||
|                 auto path = std::string {getenv("XDG_RUNTIME_DIR")} + "/somebar-0"; | ||||
|                 int fd = open(path.c_str(), O_WRONLY | O_CLOEXEC); | ||||
|                 if (fd < 0) { | ||||
|                     fprintf(stderr, "could not open %s: ", path.c_str()); | ||||
|                     perror(""); | ||||
|                     exit(1); | ||||
|                 } | ||||
|                 auto str = std::string {}; | ||||
|                 for (auto i = optind; i<argc; i++) { | ||||
|                     if (i > optind) str += " "; | ||||
|                     str += argv[i]; | ||||
|                 } | ||||
|                 str += "\n"; | ||||
|                 write(fd, str.c_str(), str.size()); | ||||
|                 exit(0); | ||||
|         } | ||||
|     } | ||||
|     static sigset_t blockedsigs; | ||||
|     sigemptyset(&blockedsigs); | ||||
|     sigaddset(&blockedsigs, SIGINT); | ||||
|     sigaddset(&blockedsigs, SIGTERM); | ||||
|     sigprocmask(SIG_BLOCK, &blockedsigs, nullptr); | ||||
| 	int opt; | ||||
| 	while ((opt = getopt(argc, argv, "chv")) != -1) { | ||||
| 		switch (opt) { | ||||
| 			case 'h': | ||||
| 				printf("Usage: %s [-h] [-v] [-c command]\n", argv[0]); | ||||
| 				printf("  -h: Show this help\n"); | ||||
| 				printf("  -v: Show somebar version\n"); | ||||
| 				printf("  -c: Sends a command to sombar. See README for details.\n"); | ||||
| 				printf("If any of these are specified, somebar exits after the action.\n"); | ||||
| 				printf("Otherwise, somebar will display itself.\n"); | ||||
| 				exit(0); | ||||
| 			case 'v': | ||||
| 				printf("somebar " SOMEBAR_VERSION "\n"); | ||||
| 				exit(0); | ||||
| 			case 'c': | ||||
| 				if (optind >= argc) { | ||||
| 					die("Expected command"); | ||||
| 				} | ||||
| 				auto path = std::string {getenv("XDG_RUNTIME_DIR")} + "/somebar-0"; | ||||
| 				int fd = open(path.c_str(), O_WRONLY | O_CLOEXEC); | ||||
| 				if (fd < 0) { | ||||
| 					fprintf(stderr, "could not open %s: ", path.c_str()); | ||||
| 					perror(""); | ||||
| 					exit(1); | ||||
| 				} | ||||
| 				auto str = std::string {}; | ||||
| 				for (auto i = optind; i<argc; i++) { | ||||
| 					if (i > optind) str += " "; | ||||
| 					str += argv[i]; | ||||
| 				} | ||||
| 				str += "\n"; | ||||
| 				write(fd, str.c_str(), str.size()); | ||||
| 				exit(0); | ||||
| 		} | ||||
| 	} | ||||
| 	static sigset_t blockedsigs; | ||||
| 	sigemptyset(&blockedsigs); | ||||
| 	sigaddset(&blockedsigs, SIGINT); | ||||
| 	sigaddset(&blockedsigs, SIGTERM); | ||||
| 	sigprocmask(SIG_BLOCK, &blockedsigs, nullptr); | ||||
| 
 | ||||
|     epoll_event epollEv = {0}; | ||||
|     std::array<epoll_event, 5> epollEvents; | ||||
|     epoll = epoll_create1(EPOLL_CLOEXEC); | ||||
|     if (epoll < 0) { | ||||
|         diesys("epoll_create1"); | ||||
|     } | ||||
|     int sfd = signalfd(-1, &blockedsigs, SFD_CLOEXEC | SFD_NONBLOCK); | ||||
|     if (sfd < 0) { | ||||
|         diesys("signalfd"); | ||||
|     } | ||||
|     epollEv.events = EPOLLIN; | ||||
|     epollEv.data.fd = sfd; | ||||
|     if (epoll_ctl(epoll, EPOLL_CTL_ADD, sfd, &epollEv) < 0) { | ||||
|         diesys("epoll_ctl add signalfd"); | ||||
|     } | ||||
| 	epoll_event epollEv = {0}; | ||||
| 	std::array<epoll_event, 5> epollEvents; | ||||
| 	epoll = epoll_create1(EPOLL_CLOEXEC); | ||||
| 	if (epoll < 0) { | ||||
| 		diesys("epoll_create1"); | ||||
| 	} | ||||
| 	int sfd = signalfd(-1, &blockedsigs, SFD_CLOEXEC | SFD_NONBLOCK); | ||||
| 	if (sfd < 0) { | ||||
| 		diesys("signalfd"); | ||||
| 	} | ||||
| 	epollEv.events = EPOLLIN; | ||||
| 	epollEv.data.fd = sfd; | ||||
| 	if (epoll_ctl(epoll, EPOLL_CTL_ADD, sfd, &epollEv) < 0) { | ||||
| 		diesys("epoll_ctl add signalfd"); | ||||
| 	} | ||||
| 
 | ||||
|     display = wl_display_connect(nullptr); | ||||
|     if (!display) { | ||||
|         die("Failed to connect to Wayland display"); | ||||
|     } | ||||
|     displayFd = wl_display_get_fd(display); | ||||
| 	display = wl_display_connect(nullptr); | ||||
| 	if (!display) { | ||||
| 		die("Failed to connect to Wayland display"); | ||||
| 	} | ||||
| 	displayFd = wl_display_get_fd(display); | ||||
| 
 | ||||
|     auto registry = wl_display_get_registry(display); | ||||
|     wl_registry_add_listener(registry, ®istry_listener, nullptr); | ||||
|     wl_display_roundtrip(display); | ||||
|     onReady(); | ||||
| 	auto registry = wl_display_get_registry(display); | ||||
| 	wl_registry_add_listener(registry, ®istry_listener, nullptr); | ||||
| 	wl_display_roundtrip(display); | ||||
| 	onReady(); | ||||
| 
 | ||||
|     epollEv.events = EPOLLIN; | ||||
|     epollEv.data.fd = displayFd; | ||||
|     if (epoll_ctl(epoll, EPOLL_CTL_ADD, displayFd, &epollEv) < 0) { | ||||
|         diesys("epoll_ctl add wayland_display"); | ||||
|     } | ||||
| 	epollEv.events = EPOLLIN; | ||||
| 	epollEv.data.fd = displayFd; | ||||
| 	if (epoll_ctl(epoll, EPOLL_CTL_ADD, displayFd, &epollEv) < 0) { | ||||
| 		diesys("epoll_ctl add wayland_display"); | ||||
| 	} | ||||
| 
 | ||||
|     while (!quitting) { | ||||
|         waylandFlush(); | ||||
|         auto res = epoll_wait(epoll, epollEvents.data(), epollEvents.size(), -1); | ||||
|         if (res < 0) { | ||||
|             if (errno != EINTR) { | ||||
|                 diesys("epoll_wait"); | ||||
|             } | ||||
|         } else { | ||||
|             for (auto i=0; i<res; i++) { | ||||
|                 auto &ev = epollEvents[i]; | ||||
|                 if (ev.data.fd == displayFd) { | ||||
|                     if (ev.events & EPOLLIN) { | ||||
|                         if (wl_display_dispatch(display) < 0) { | ||||
|                             die("wl_display_dispatch"); | ||||
|                         } | ||||
|                     } if (ev.events & EPOLLOUT) { | ||||
|                         epollEv.events = EPOLLIN; | ||||
|                         epollEv.data.fd = displayFd; | ||||
|                         if (epoll_ctl(epoll, EPOLL_CTL_MOD, displayFd, &epollEv) < 0) { | ||||
|                             diesys("epoll_ctl"); | ||||
|                         } | ||||
|                         waylandFlush(); | ||||
|                     } | ||||
|                 } else if (ev.data.fd == statusFifoFd) { | ||||
|                     onStatus(); | ||||
|                 } else if (ev.data.fd == sfd) { | ||||
|                     quitting = true; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     cleanup(); | ||||
| 	while (!quitting) { | ||||
| 		waylandFlush(); | ||||
| 		auto res = epoll_wait(epoll, epollEvents.data(), epollEvents.size(), -1); | ||||
| 		if (res < 0) { | ||||
| 			if (errno != EINTR) { | ||||
| 				diesys("epoll_wait"); | ||||
| 			} | ||||
| 		} else { | ||||
| 			for (auto i=0; i<res; i++) { | ||||
| 				auto &ev = epollEvents[i]; | ||||
| 				if (ev.data.fd == displayFd) { | ||||
| 					if (ev.events & EPOLLIN) { | ||||
| 						if (wl_display_dispatch(display) < 0) { | ||||
| 							die("wl_display_dispatch"); | ||||
| 						} | ||||
| 					} if (ev.events & EPOLLOUT) { | ||||
| 						epollEv.events = EPOLLIN; | ||||
| 						epollEv.data.fd = displayFd; | ||||
| 						if (epoll_ctl(epoll, EPOLL_CTL_MOD, displayFd, &epollEv) < 0) { | ||||
| 							diesys("epoll_ctl"); | ||||
| 						} | ||||
| 						waylandFlush(); | ||||
| 					} | ||||
| 				} else if (ev.data.fd == statusFifoFd) { | ||||
| 					onStatus(); | ||||
| 				} else if (ev.data.fd == sfd) { | ||||
| 					quitting = true; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	cleanup(); | ||||
| } | ||||
| 
 | ||||
| void requireGlobal(const void *p, const char *name) | ||||
| { | ||||
|     if (p) return; | ||||
|     fprintf(stderr, "Wayland compositor does not export required global %s, aborting.\n", name); | ||||
|     cleanup(); | ||||
|     exit(1); | ||||
| 	if (p) return; | ||||
| 	fprintf(stderr, "Wayland compositor does not export required global %s, aborting.\n", name); | ||||
| 	cleanup(); | ||||
| 	exit(1); | ||||
| } | ||||
| 
 | ||||
| void waylandFlush() | ||||
| { | ||||
|     wl_display_dispatch_pending(display); | ||||
|     if (wl_display_flush(display) < 0 && errno == EAGAIN) { | ||||
|         epoll_event ev = {0}; | ||||
|         ev.events = EPOLLIN | EPOLLOUT; | ||||
|         ev.data.fd = displayFd; | ||||
|         if (epoll_ctl(epoll, EPOLL_CTL_MOD, displayFd, &ev) < 0) { | ||||
|             diesys("epoll_ctl"); | ||||
|         } | ||||
|     } | ||||
| 	wl_display_dispatch_pending(display); | ||||
| 	if (wl_display_flush(display) < 0 && errno == EAGAIN) { | ||||
| 		epoll_event ev = {0}; | ||||
| 		ev.events = EPOLLIN | EPOLLOUT; | ||||
| 		ev.data.fd = displayFd; | ||||
| 		if (epoll_ctl(epoll, EPOLL_CTL_MOD, displayFd, &ev) < 0) { | ||||
| 			diesys("epoll_ctl"); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void die(const char* why) { | ||||
|     fprintf(stderr, "%s\n", why); | ||||
|     cleanup(); | ||||
|     exit(1); | ||||
| 	fprintf(stderr, "%s\n", why); | ||||
| 	cleanup(); | ||||
| 	exit(1); | ||||
| } | ||||
| 
 | ||||
| void diesys(const char* why) { | ||||
|     perror(why); | ||||
|     cleanup(); | ||||
|     exit(1); | ||||
| 	perror(why); | ||||
| 	cleanup(); | ||||
| 	exit(1); | ||||
| } | ||||
| 
 | ||||
| void cleanup() { | ||||
|     if (!statusFifoName.empty()) { | ||||
|         unlink(statusFifoName.c_str()); | ||||
|     } | ||||
| 	if (!statusFifoName.empty()) { | ||||
| 		unlink(statusFifoName.c_str()); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -9,26 +9,26 @@ | ||||
| constexpr int n = 2; | ||||
| 
 | ||||
| ShmBuffer::ShmBuffer(int w, int h, wl_shm_format format) | ||||
|     : width(w) | ||||
|     , height(h) | ||||
|     , stride(w*4) | ||||
| 	: width(w) | ||||
| 	, height(h) | ||||
| 	, stride(w*4) | ||||
| { | ||||
|     auto oneSize = stride*size_t(h); | ||||
|     auto totalSize = oneSize * n; | ||||
|     auto fd = memfd_create("wl_shm", MFD_CLOEXEC); | ||||
|     ftruncate(fd, totalSize); | ||||
|     auto pool = wl_shm_create_pool(shm, fd, totalSize); | ||||
|     auto ptr = reinterpret_cast<uint8_t*>(mmap(nullptr, totalSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); | ||||
|     _mapping = MemoryMapping {ptr, totalSize}; | ||||
|     close(fd); | ||||
|     for (auto i=0; i<n; i++) { | ||||
|         auto offset = oneSize*i; | ||||
|         _buffers[i] = { | ||||
|             ptr+offset, | ||||
|             wl_unique_ptr<wl_buffer> { wl_shm_pool_create_buffer(pool, offset, width, height, stride, format) }, | ||||
|         }; | ||||
|     } | ||||
|     wl_shm_pool_destroy(pool); | ||||
| 	auto oneSize = stride*size_t(h); | ||||
| 	auto totalSize = oneSize * n; | ||||
| 	auto fd = memfd_create("wl_shm", MFD_CLOEXEC); | ||||
| 	ftruncate(fd, totalSize); | ||||
| 	auto pool = wl_shm_create_pool(shm, fd, totalSize); | ||||
| 	auto ptr = reinterpret_cast<uint8_t*>(mmap(nullptr, totalSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); | ||||
| 	_mapping = MemoryMapping {ptr, totalSize}; | ||||
| 	close(fd); | ||||
| 	for (auto i=0; i<n; i++) { | ||||
| 		auto offset = oneSize*i; | ||||
| 		_buffers[i] = { | ||||
| 			ptr+offset, | ||||
| 			wl_unique_ptr<wl_buffer> { wl_shm_pool_create_buffer(pool, offset, width, height, stride, format) }, | ||||
| 		}; | ||||
| 	} | ||||
| 	wl_shm_pool_destroy(pool); | ||||
| } | ||||
| 
 | ||||
| uint8_t* ShmBuffer::data() { return _buffers[_current].data; } | ||||
|  | ||||
| @ -8,38 +8,38 @@ | ||||
| #include "common.hpp" | ||||
| 
 | ||||
| class MemoryMapping { | ||||
|     void* _ptr {nullptr}; | ||||
|     size_t _size {0}; | ||||
| 	void* _ptr {nullptr}; | ||||
| 	size_t _size {0}; | ||||
| public: | ||||
|     MemoryMapping() { } | ||||
|     explicit MemoryMapping(void* ptr, size_t size) : _ptr(ptr), _size(size) { } | ||||
|     MemoryMapping(const MemoryMapping&) = delete; | ||||
|     MemoryMapping(MemoryMapping&& other) { swap(other); } | ||||
|     MemoryMapping& operator=(const MemoryMapping& other) = delete; | ||||
|     MemoryMapping& operator=(MemoryMapping&& other) { swap(other); return *this; } | ||||
|     ~MemoryMapping() { if (_ptr) munmap(_ptr, _size); } | ||||
|     void swap(MemoryMapping &other) { | ||||
|         using std::swap; | ||||
|         swap(_ptr, other._ptr); | ||||
|         swap(_size, other._size); | ||||
|     } | ||||
| 	MemoryMapping() { } | ||||
| 	explicit MemoryMapping(void* ptr, size_t size) : _ptr(ptr), _size(size) { } | ||||
| 	MemoryMapping(const MemoryMapping&) = delete; | ||||
| 	MemoryMapping(MemoryMapping&& other) { swap(other); } | ||||
| 	MemoryMapping& operator=(const MemoryMapping& other) = delete; | ||||
| 	MemoryMapping& operator=(MemoryMapping&& other) { swap(other); return *this; } | ||||
| 	~MemoryMapping() { if (_ptr) munmap(_ptr, _size); } | ||||
| 	void swap(MemoryMapping &other) { | ||||
| 		using std::swap; | ||||
| 		swap(_ptr, other._ptr); | ||||
| 		swap(_size, other._size); | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| // double buffered shm
 | ||||
| // format is must be 32-bit
 | ||||
| class ShmBuffer { | ||||
|     struct Buf { | ||||
|         uint8_t* data {nullptr}; | ||||
|         wl_unique_ptr<wl_buffer> buffer; | ||||
|     }; | ||||
|     std::array<Buf, 2> _buffers; | ||||
|     int _current {0}; | ||||
|     MemoryMapping _mapping; | ||||
| 	struct Buf { | ||||
| 		uint8_t* data {nullptr}; | ||||
| 		wl_unique_ptr<wl_buffer> buffer; | ||||
| 	}; | ||||
| 	std::array<Buf, 2> _buffers; | ||||
| 	int _current {0}; | ||||
| 	MemoryMapping _mapping; | ||||
| public: | ||||
|     int width, height, stride; | ||||
| 	int width, height, stride; | ||||
| 
 | ||||
|     explicit ShmBuffer(int width, int height, wl_shm_format format); | ||||
|     uint8_t* data(); | ||||
|     wl_buffer* buffer(); | ||||
|     void flip(); | ||||
| 	explicit ShmBuffer(int width, int height, wl_shm_format format); | ||||
| 	uint8_t* data(); | ||||
| 	wl_buffer* buffer(); | ||||
| 	void flip(); | ||||
| }; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user