style: spaces -> tabs

This commit is contained in:
Raphael Robatsch 2021-10-29 20:33:27 +02:00
parent 7b3700e730
commit 0f81338bb6
11 changed files with 876 additions and 876 deletions

View File

@ -1,9 +1,9 @@
project('somebar', ['c', 'cpp'], project('somebar', ['c', 'cpp'],
version: '0.1.0', version: '0.1.0',
default_options: [ default_options: [
'cpp_std=c++17', 'cpp_std=c++17',
'cpp_args=-Wno-parentheses', 'cpp_args=-Wno-parentheses',
]) ])
wayland_dep = dependency('wayland-client') wayland_dep = dependency('wayland-client')
wayland_cursor_dep = dependency('wayland-cursor') wayland_cursor_dep = dependency('wayland-cursor')
@ -14,16 +14,16 @@ pangocairo_dep = dependency('pangocairo')
subdir('protocols') subdir('protocols')
executable('somebar', executable('somebar',
'src/main.cpp', 'src/main.cpp',
'src/shm_buffer.cpp', 'src/shm_buffer.cpp',
'src/bar.cpp', 'src/bar.cpp',
wayland_sources, wayland_sources,
dependencies: [ dependencies: [
wayland_dep, wayland_dep,
wayland_cursor_dep, wayland_cursor_dep,
cairo_dep, cairo_dep,
pango_dep, pango_dep,
pangocairo_dep, pangocairo_dep,
], ],
install: true, install: true,
cpp_args: '-DSOMEBAR_VERSION="@0@"'.format(meson.project_version())) cpp_args: '-DSOMEBAR_VERSION="@0@"'.format(meson.project_version()))

View File

@ -3,21 +3,21 @@ wayland_scanner = find_program('wayland-scanner')
wayland_protos_dep = dependency('wayland-protocols') wayland_protos_dep = dependency('wayland-protocols')
wl_protocol_dir = wayland_protos_dep.get_pkgconfig_variable('pkgdatadir') wl_protocol_dir = wayland_protos_dep.get_pkgconfig_variable('pkgdatadir')
wayland_scanner_code = generator( wayland_scanner_code = generator(
wayland_scanner, wayland_scanner,
output: '@BASENAME@-protocol.c', output: '@BASENAME@-protocol.c',
arguments: ['private-code', '@INPUT@', '@OUTPUT@']) arguments: ['private-code', '@INPUT@', '@OUTPUT@'])
wayland_scanner_client = generator( wayland_scanner_client = generator(
wayland_scanner, wayland_scanner,
output: '@BASENAME@-client-protocol.h', output: '@BASENAME@-client-protocol.h',
arguments: ['client-header', '@INPUT@', '@OUTPUT@']) arguments: ['client-header', '@INPUT@', '@OUTPUT@'])
wayland_xmls = [ wayland_xmls = [
wl_protocol_dir + '/stable/xdg-shell/xdg-shell.xml', wl_protocol_dir + '/stable/xdg-shell/xdg-shell.xml',
wl_protocol_dir + '/unstable/xdg-output/xdg-output-unstable-v1.xml', wl_protocol_dir + '/unstable/xdg-output/xdg-output-unstable-v1.xml',
'wlr-layer-shell-unstable-v1.xml', 'wlr-layer-shell-unstable-v1.xml',
'net-tapesoftware-dwl-wm-unstable-v1.xml', 'net-tapesoftware-dwl-wm-unstable-v1.xml',
] ]
wayland_sources = [ wayland_sources = [
wayland_scanner_code.process(wayland_xmls), wayland_scanner_code.process(wayland_xmls),
wayland_scanner_client.process(wayland_xmls), wayland_scanner_client.process(wayland_xmls),
] ]

View File

@ -1,164 +1,164 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<protocol name="net_tapesoftware_dwl_wm_unstable_v1"> <protocol name="net_tapesoftware_dwl_wm_unstable_v1">
<copyright> <copyright>
Copyright (c) 2021 Raphael Robatsch Copyright (c) 2021 Raphael Robatsch
Permission is hereby granted, free of charge, to any person obtaining a Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
the following conditions: the following conditions:
The above copyright notice and this permission notice (including the The above copyright notice and this permission notice (including the
next paragraph) shall be included in all copies or substantial portions next paragraph) shall be included in all copies or substantial portions
of the Software. of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
</copyright> </copyright>
<interface name="znet_tapesoftware_dwl_wm_v1" version="1"> <interface name="znet_tapesoftware_dwl_wm_v1" version="1">
<description summary="control the dwl state"> <description summary="control the dwl state">
This interface is exposed as a global in the wl_registry. This interface is exposed as a global in the wl_registry.
Clients can use this protocol to receive updates of the window manager Clients can use this protocol to receive updates of the window manager
state (active tags, active layout, and focused window). state (active tags, active layout, and focused window).
Clients can also control this state. Clients can also control this state.
After binding, the client will receive the available tags and layouts After binding, the client will receive the available tags and layouts
with the 'tag' and 'layout' events. These can be used in subsequent 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.set_tags/set_layout requests, and to interpret the
dwl_wm_monitor_v1.layout/tag events. dwl_wm_monitor_v1.layout/tag events.
</description> </description>
<request name="release" type="destructor"> <request name="release" type="destructor">
<description summary="release dwl_wm"> <description summary="release dwl_wm">
This request indicates that the client will not use the 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 object any more. Objects that have been created through this instance
are not affected. are not affected.
</description> </description>
</request> </request>
<request name="get_monitor"> <request name="get_monitor">
<description summary="gets a dwl monitor from an output"> <description summary="gets a dwl monitor from an output">
Gets a dwl monitor for the specified output. The window manager Gets a dwl monitor for the specified output. The window manager
state on the output can be controlled using the monitor. state on the output can be controlled using the monitor.
</description> </description>
<arg name="id" type="new_id" interface="znet_tapesoftware_dwl_wm_monitor_v1" /> <arg name="id" type="new_id" interface="znet_tapesoftware_dwl_wm_monitor_v1" />
<arg name="output" type="object" interface="wl_output" /> <arg name="output" type="object" interface="wl_output" />
</request> </request>
<event name="tag"> <event name="tag">
<description summary="announces the presence of a tag"> <description summary="announces the presence of a tag">
This event is sent immediately after binding. This event is sent immediately after binding.
A roundtrip after binding guarantees that the client has received all tags. A roundtrip after binding guarantees that the client has received all tags.
</description> </description>
<arg name="name" type="string"/> <arg name="name" type="string"/>
</event> </event>
<event name="layout"> <event name="layout">
<description summary="announces the presence of a layout"> <description summary="announces the presence of a layout">
This event is sent immediately after binding. This event is sent immediately after binding.
A roundtrip after binding guarantees that the client has received all layouts. A roundtrip after binding guarantees that the client has received all layouts.
</description> </description>
<arg name="name" type="string"/> <arg name="name" type="string"/>
</event> </event>
</interface> </interface>
<interface name="znet_tapesoftware_dwl_wm_monitor_v1" version="1"> <interface name="znet_tapesoftware_dwl_wm_monitor_v1" version="1">
<description summary="control one monitor"> <description summary="control one monitor">
Observes and controls one monitor. Observes and controls one monitor.
Events are double-buffered: Clients should cache all events and only Events are double-buffered: Clients should cache all events and only
redraw themselves once the 'frame' event is sent. redraw themselves once the 'frame' event is sent.
Requests are not double-buffered: The compositor will update itself Requests are not double-buffered: The compositor will update itself
immediately. immediately.
</description> </description>
<enum name="tag_state"> <enum name="tag_state">
<entry name="none" value="0" summary="no state"/> <entry name="none" value="0" summary="no state"/>
<entry name="active" value="1" summary="tag is active"/> <entry name="active" value="1" summary="tag is active"/>
<entry name="urgent" value="2" summary="tag has at least one urgent client"/> <entry name="urgent" value="2" summary="tag has at least one urgent client"/>
</enum> </enum>
<request name="release" type="destructor"> <request name="release" type="destructor">
<description summary="release dwl_monitor"> <description summary="release dwl_monitor">
This request indicates that the client is done with this dwl_monitor. This request indicates that the client is done with this dwl_monitor.
All further requests are ignored. All further requests are ignored.
</description> </description>
</request> </request>
<event name="selected"> <event name="selected">
<description summary="updates the selected state of the monitor"> <description summary="updates the selected state of the monitor">
If 'selected' is nonzero, this monitor is the currently selected one. If 'selected' is nonzero, this monitor is the currently selected one.
</description> </description>
<arg name="selected" type="uint"/> <arg name="selected" type="uint"/>
</event> </event>
<event name="tag"> <event name="tag">
<description summary="updates the state of one tag"> <description summary="updates the state of one tag">
Announces the update of a tag. num_clients and focused_client can be Announces the update of a tag. num_clients and focused_client can be
used to draw client indicators. used to draw client indicators.
</description> </description>
<arg name="tag" type="uint" summary="index of a tag received by the dwl_wm_v1.tag event." /> <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="state" type="uint" enum="tag_state"/>
<arg name="num_clients" type="uint" summary="number of clients on this tag"/> <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"/> <arg name="focused_client" type="int" summary="out of num_clients. -1 if there is no focused client"/>
</event> </event>
<event name="layout"> <event name="layout">
<description summary="updates the selected layout"> <description summary="updates the selected layout">
Announces the update of the selected layout. Announces the update of the selected layout.
</description> </description>
<arg name="layout" type="uint" summary="index of a layout received by the dwl_wm_v1.layout event."/> <arg name="layout" type="uint" summary="index of a layout received by the dwl_wm_v1.layout event."/>
</event> </event>
<event name="title"> <event name="title">
<description summary="updates the focused client"> <description summary="updates the focused client">
Announces the update of the selected client. Announces the update of the selected client.
</description> </description>
<arg name="title" type="string"/> <arg name="title" type="string"/>
</event> </event>
<event name="frame"> <event name="frame">
<description summary="end of status update sequence"> <description summary="end of status update sequence">
Sent after all other events belonging to the status update has been sent. Sent after all other events belonging to the status update has been sent.
Clients should redraw themselves now. Clients should redraw themselves now.
</description> </description>
</event> </event>
<request name="set_tags"> <request name="set_tags">
<description summary="sets the active tags on this monitor."> <description summary="sets the active tags on this monitor.">
Changes are applied immediately. Changes are applied immediately.
</description> </description>
<arg name="tagmask" type="uint" summary="bitmask of the tags that should be set."/> <arg name="tagmask" type="uint" summary="bitmask of the tags that should be set."/>
<arg name="toggle_tagset" type="uint"/> <arg name="toggle_tagset" type="uint"/>
</request> </request>
<request name="set_client_tags"> <request name="set_client_tags">
<description summary="updates the tags of the focused client."> <description summary="updates the tags of the focused client.">
tags are updated as follows: tags are updated as follows:
new_tags = (current_tags AND and_tags) XOR xor_tags new_tags = (current_tags AND and_tags) XOR xor_tags
Changes are applied immediately. Changes are applied immediately.
</description> </description>
<arg name="and_tags" type="uint"/> <arg name="and_tags" type="uint"/>
<arg name="xor_tags" type="uint"/> <arg name="xor_tags" type="uint"/>
</request> </request>
<request name="set_layout"> <request name="set_layout">
<description summary="sets the active layout on this monitor."> <description summary="sets the active layout on this monitor.">
Changes are applied immediately. Changes are applied immediately.
</description> </description>
<arg name="layout" type="uint" summary="index of a layout received by the dwl_wm_v1.layout event."/> <arg name="layout" type="uint" summary="index of a layout received by the dwl_wm_v1.layout event."/>
</request> </request>
</interface> </interface>
</protocol> </protocol>

View File

@ -11,44 +11,44 @@
#include "pango/pango-layout.h" #include "pango/pango-layout.h"
const zwlr_layer_surface_v1_listener Bar::_layerSurfaceListener = { const zwlr_layer_surface_v1_listener Bar::_layerSurfaceListener = {
[](void* owner, zwlr_layer_surface_v1*, uint32_t serial, uint32_t width, uint32_t height) [](void* owner, zwlr_layer_surface_v1*, uint32_t serial, uint32_t width, uint32_t height)
{ {
static_cast<Bar*>(owner)->layerSurfaceConfigure(serial, width, height); static_cast<Bar*>(owner)->layerSurfaceConfigure(serial, width, height);
} }
}; };
const wl_callback_listener Bar::_frameListener = { const wl_callback_listener Bar::_frameListener = {
[](void* owner, wl_callback* cb, uint32_t) [](void* owner, wl_callback* cb, uint32_t)
{ {
static_cast<Bar*>(owner)->render(); static_cast<Bar*>(owner)->render();
wl_callback_destroy(cb); wl_callback_destroy(cb);
} }
}; };
struct Font { struct Font {
PangoFontDescription* description; PangoFontDescription* description;
int height {0}; int height {0};
}; };
static Font getFont() static Font getFont()
{ {
auto fontMap = pango_cairo_font_map_get_default(); auto fontMap = pango_cairo_font_map_get_default();
if (!fontMap) die("pango_cairo_font_map_get_default"); if (!fontMap) die("pango_cairo_font_map_get_default");
auto fontDesc = pango_font_description_from_string(font); auto fontDesc = pango_font_description_from_string(font);
if (!fontDesc) die("pango_font_description_from_string"); if (!fontDesc) die("pango_font_description_from_string");
auto tempContext = pango_font_map_create_context(fontMap); auto tempContext = pango_font_map_create_context(fontMap);
if (!tempContext) die("pango_font_map_create_context"); if (!tempContext) die("pango_font_map_create_context");
auto font = pango_font_map_load_font(fontMap, tempContext, fontDesc); auto font = pango_font_map_load_font(fontMap, tempContext, fontDesc);
if (!font) die("pango_font_map_load_font"); if (!font) die("pango_font_map_load_font");
auto metrics = pango_font_get_metrics(font, pango_language_get_default()); auto metrics = pango_font_get_metrics(font, pango_language_get_default());
if (!metrics) die("pango_font_get_metrics"); if (!metrics) die("pango_font_get_metrics");
auto res = Font {}; auto res = Font {};
res.description = fontDesc; res.description = fontDesc;
res.height = PANGO_PIXELS(pango_font_metrics_get_height(metrics)); res.height = PANGO_PIXELS(pango_font_metrics_get_height(metrics));
pango_font_metrics_unref(metrics); pango_font_metrics_unref(metrics);
g_object_unref(font); g_object_unref(font);
g_object_unref(tempContext); g_object_unref(tempContext);
return res; return res;
} }
static Font barfont = getFont(); static Font barfont = getFont();
@ -56,27 +56,27 @@ BarComponent::BarComponent() { }
BarComponent::BarComponent(wl_unique_ptr<PangoLayout> layout) : pangoLayout {std::move(layout)} {} BarComponent::BarComponent(wl_unique_ptr<PangoLayout> layout) : pangoLayout {std::move(layout)} {}
int BarComponent::width() const int BarComponent::width() const
{ {
int w, h; int w, h;
pango_layout_get_size(pangoLayout.get(), &w, &h); pango_layout_get_size(pangoLayout.get(), &w, &h);
return PANGO_PIXELS(w); return PANGO_PIXELS(w);
} }
void BarComponent::setText(const std::string& text) void BarComponent::setText(const std::string& text)
{ {
_text = std::make_unique<std::string>(text); _text = std::make_unique<std::string>(text);
pango_layout_set_text(pangoLayout.get(), _text->c_str(), _text->size()); pango_layout_set_text(pangoLayout.get(), _text->c_str(), _text->size());
} }
Bar::Bar(Monitor* mon) Bar::Bar(Monitor* mon)
{ {
_mon = mon; _mon = mon;
_pangoContext.reset(pango_font_map_create_context(pango_cairo_font_map_get_default())); _pangoContext.reset(pango_font_map_create_context(pango_cairo_font_map_get_default()));
if (!_pangoContext) die("pango_font_map_create_context"); if (!_pangoContext) die("pango_font_map_create_context");
for (auto i=0u; i<tagNames.size(); i++) { 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]) }); _tags.push_back({ ZNET_TAPESOFTWARE_DWL_WM_MONITOR_V1_TAG_STATE_NONE, 0, 0, createComponent(tagNames[i]) });
} }
_layoutCmp = createComponent(); _layoutCmp = createComponent();
_titleCmp = createComponent(); _titleCmp = createComponent();
_statusCmp = createComponent(); _statusCmp = createComponent();
} }
const wl_surface* Bar::surface() const { return _surface.get(); } 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) void Bar::show(wl_output* output)
{ {
if (visible()) return; if (visible()) return;
_surface.reset(wl_compositor_create_surface(compositor)); _surface.reset(wl_compositor_create_surface(compositor));
_layerSurface.reset(zwlr_layer_shell_v1_get_layer_surface(wlrLayerShell, _layerSurface.reset(zwlr_layer_shell_v1_get_layer_surface(wlrLayerShell,
_surface.get(), nullptr, ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "net.tapesoftware.Somebar")); _surface.get(), nullptr, ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "net.tapesoftware.Somebar"));
zwlr_layer_surface_v1_add_listener(_layerSurface.get(), &_layerSurfaceListener, this); 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; auto anchor = topbar ? ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP : ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
zwlr_layer_surface_v1_set_anchor(_layerSurface.get(), zwlr_layer_surface_v1_set_anchor(_layerSurface.get(),
anchor | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT); anchor | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT);
auto barSize = barfont.height + paddingY * 2; auto barSize = barfont.height + paddingY * 2;
zwlr_layer_surface_v1_set_size(_layerSurface.get(), 0, barSize); zwlr_layer_surface_v1_set_size(_layerSurface.get(), 0, barSize);
zwlr_layer_surface_v1_set_exclusive_zone(_layerSurface.get(), barSize); zwlr_layer_surface_v1_set_exclusive_zone(_layerSurface.get(), barSize);
wl_surface_commit(_surface.get()); wl_surface_commit(_surface.get());
} }
void Bar::hide() void Bar::hide()
{ {
if (!visible()) return; if (!visible()) return;
_layerSurface.reset(); _layerSurface.reset();
_surface.reset(); _surface.reset();
_bufs.reset(); _bufs.reset();
} }
void Bar::setTag(int tag, znet_tapesoftware_dwl_wm_monitor_v1_tag_state state, int numClients, int focusedClient) void Bar::setTag(int tag, znet_tapesoftware_dwl_wm_monitor_v1_tag_state state, int numClients, int focusedClient)
{ {
auto& t = _tags[tag]; auto& t = _tags[tag];
t.state = state; t.state = state;
t.numClients = numClients; t.numClients = numClients;
t.focusedClient = focusedClient; t.focusedClient = focusedClient;
} }
void Bar::setSelected(bool selected) { _selected = selected; } void Bar::setSelected(bool selected) { _selected = selected; }
void Bar::setLayout(int layout) { _layoutCmp.setText(layoutNames[layout]); } 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() void Bar::invalidate()
{ {
if (_invalid || !visible()) return; if (_invalid || !visible()) return;
_invalid = true; _invalid = true;
auto frame = wl_surface_frame(_surface.get()); auto frame = wl_surface_frame(_surface.get());
wl_callback_add_listener(frame, &_frameListener, this); wl_callback_add_listener(frame, &_frameListener, this);
wl_surface_commit(_surface.get()); wl_surface_commit(_surface.get());
} }
void Bar::click(int x, int, int btn) void Bar::click(int x, int, int btn)
{ {
Arg arg = {0}; Arg arg = {0};
Arg* argp = nullptr; Arg* argp = nullptr;
int control = ClkNone; int control = ClkNone;
if (x > _statusCmp.x) { if (x > _statusCmp.x) {
control = ClkStatusText; control = ClkStatusText;
} else if (x > _titleCmp.x) { } else if (x > _titleCmp.x) {
control = ClkWinTitle; control = ClkWinTitle;
} else if (x > _layoutCmp.x) { } else if (x > _layoutCmp.x) {
control = ClkLayoutSymbol; control = ClkLayoutSymbol;
} else for (auto tag = _tags.size()-1; tag >= 0; tag--) { } else for (auto tag = _tags.size()-1; tag >= 0; tag--) {
if (x > _tags[tag].component.x) { if (x > _tags[tag].component.x) {
control = ClkTagBar; control = ClkTagBar;
arg.ui = 1<<tag; arg.ui = 1<<tag;
argp = &arg; argp = &arg;
break; break;
} }
} }
for (auto i = 0u; i < sizeof(buttons)/sizeof(buttons[0]); i++) { for (auto i = 0u; i < sizeof(buttons)/sizeof(buttons[0]); i++) {
const auto& button = buttons[i]; const auto& button = buttons[i];
if (button.control == control && button.btn == btn) { if (button.control == control && button.btn == btn) {
button.func(*_mon, *(argp ? argp : &button.arg)); button.func(*_mon, *(argp ? argp : &button.arg));
return; return;
} }
} }
} }
void Bar::layerSurfaceConfigure(uint32_t serial, uint32_t width, uint32_t height) void Bar::layerSurfaceConfigure(uint32_t serial, uint32_t width, uint32_t height)
{ {
zwlr_layer_surface_v1_ack_configure(_layerSurface.get(), serial); zwlr_layer_surface_v1_ack_configure(_layerSurface.get(), serial);
_bufs.emplace(width, height, WL_SHM_FORMAT_XRGB8888); _bufs.emplace(width, height, WL_SHM_FORMAT_XRGB8888);
render(); render();
} }
void Bar::render() void Bar::render()
{ {
if (!visible()) return; if (!visible()) return;
auto img = wl_unique_ptr<cairo_surface_t> {cairo_image_surface_create_for_data( auto img = wl_unique_ptr<cairo_surface_t> {cairo_image_surface_create_for_data(
_bufs->data(), _bufs->data(),
CAIRO_FORMAT_ARGB32, CAIRO_FORMAT_ARGB32,
_bufs->width, _bufs->width,
_bufs->height, _bufs->height,
_bufs->stride _bufs->stride
)}; )};
auto painter = wl_unique_ptr<cairo_t> {cairo_create(img.get())}; auto painter = wl_unique_ptr<cairo_t> {cairo_create(img.get())};
_painter = painter.get(); _painter = painter.get();
pango_cairo_update_context(_painter, _pangoContext.get()); pango_cairo_update_context(_painter, _pangoContext.get());
_x = 0; _x = 0;
renderTags(); renderTags();
setColorScheme(_selected ? colorActive : colorInactive); setColorScheme(_selected ? colorActive : colorInactive);
renderComponent(_layoutCmp); renderComponent(_layoutCmp);
renderComponent(_titleCmp); renderComponent(_titleCmp);
renderStatus(); renderStatus();
_painter = nullptr; _painter = nullptr;
wl_surface_attach(_surface.get(), _bufs->buffer(), 0, 0); wl_surface_attach(_surface.get(), _bufs->buffer(), 0, 0);
wl_surface_damage(_surface.get(), 0, 0, INT_MAX, INT_MAX); wl_surface_damage(_surface.get(), 0, 0, INT_MAX, INT_MAX);
wl_surface_commit(_surface.get()); wl_surface_commit(_surface.get());
_bufs->flip(); _bufs->flip();
_invalid = false; _invalid = false;
} }
void Bar::renderTags() void Bar::renderTags()
{ {
for (auto &tag : _tags) { for (auto &tag : _tags) {
setColorScheme( setColorScheme(
tag.state & ZNET_TAPESOFTWARE_DWL_WM_MONITOR_V1_TAG_STATE_ACTIVE ? colorActive : colorInactive, tag.state & ZNET_TAPESOFTWARE_DWL_WM_MONITOR_V1_TAG_STATE_ACTIVE ? colorActive : colorInactive,
tag.state & ZNET_TAPESOFTWARE_DWL_WM_MONITOR_V1_TAG_STATE_URGENT); tag.state & ZNET_TAPESOFTWARE_DWL_WM_MONITOR_V1_TAG_STATE_URGENT);
renderComponent(tag.component); renderComponent(tag.component);
auto indicators = std::min(tag.numClients, _bufs->height/2); auto indicators = std::min(tag.numClients, _bufs->height/2);
for (auto ind = 0; ind < indicators; ind++) { for (auto ind = 0; ind < indicators; ind++) {
auto w = ind == tag.focusedClient ? 7 : 1; auto w = ind == tag.focusedClient ? 7 : 1;
cairo_move_to(_painter, tag.component.x, ind*2+0.5); cairo_move_to(_painter, tag.component.x, ind*2+0.5);
cairo_rel_line_to(_painter, w, 0); cairo_rel_line_to(_painter, w, 0);
cairo_close_path(_painter); cairo_close_path(_painter);
cairo_set_line_width(_painter, 1); cairo_set_line_width(_painter, 1);
cairo_stroke(_painter); cairo_stroke(_painter);
} }
} }
} }
void Bar::renderStatus() void Bar::renderStatus()
{ {
pango_cairo_update_layout(_painter, _statusCmp.pangoLayout.get()); pango_cairo_update_layout(_painter, _statusCmp.pangoLayout.get());
beginBg(); beginBg();
auto start = _bufs->width - _statusCmp.width() - paddingX*2; auto start = _bufs->width - _statusCmp.width() - paddingX*2;
cairo_rectangle(_painter, _x, 0, _bufs->width-_x+start, _bufs->height); cairo_rectangle(_painter, _x, 0, _bufs->width-_x+start, _bufs->height);
cairo_fill(_painter); cairo_fill(_painter);
_x = start; _x = start;
renderComponent(_statusCmp); renderComponent(_statusCmp);
} }
void Bar::setColorScheme(const ColorScheme& scheme, bool invert) void Bar::setColorScheme(const ColorScheme& scheme, bool invert)
{ {
_colorScheme = invert _colorScheme = invert
? ColorScheme {scheme.bg, scheme.fg} ? ColorScheme {scheme.bg, scheme.fg}
: ColorScheme {scheme.fg, scheme.bg}; : ColorScheme {scheme.fg, scheme.bg};
} }
static void setColor(cairo_t* painter, const Color& color) 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::beginFg() { setColor(_painter, _colorScheme.fg); }
void Bar::beginBg() { setColor(_painter, _colorScheme.bg); } void Bar::beginBg() { setColor(_painter, _colorScheme.bg); }
void Bar::renderComponent(BarComponent& component) void Bar::renderComponent(BarComponent& component)
{ {
pango_cairo_update_layout(_painter, component.pangoLayout.get()); pango_cairo_update_layout(_painter, component.pangoLayout.get());
auto size = component.width() + paddingX*2; auto size = component.width() + paddingX*2;
component.x = _x; component.x = _x;
beginBg(); beginBg();
cairo_rectangle(_painter, _x, 0, size, _bufs->height); cairo_rectangle(_painter, _x, 0, size, _bufs->height);
cairo_fill(_painter); cairo_fill(_painter);
cairo_move_to(_painter, _x+paddingX, paddingY); cairo_move_to(_painter, _x+paddingX, paddingY);
beginFg(); beginFg();
pango_cairo_show_layout(_painter, component.pangoLayout.get()); pango_cairo_show_layout(_painter, component.pangoLayout.get());
_x += size; _x += size;
} }
BarComponent Bar::createComponent(const std::string &initial) BarComponent Bar::createComponent(const std::string &initial)
{ {
auto layout = pango_layout_new(_pangoContext.get()); auto layout = pango_layout_new(_pangoContext.get());
pango_layout_set_font_description(layout, barfont.description); pango_layout_set_font_description(layout, barfont.description);
auto res = BarComponent {wl_unique_ptr<PangoLayout> {layout}}; auto res = BarComponent {wl_unique_ptr<PangoLayout> {layout}};
res.setText(initial); res.setText(initial);
return res; return res;
} }

View File

@ -11,65 +11,65 @@
#include "shm_buffer.hpp" #include "shm_buffer.hpp"
class BarComponent { class BarComponent {
std::unique_ptr<std::string> _text; std::unique_ptr<std::string> _text;
public: public:
BarComponent(); BarComponent();
explicit BarComponent(wl_unique_ptr<PangoLayout> layout); explicit BarComponent(wl_unique_ptr<PangoLayout> layout);
int width() const; int width() const;
void setText(const std::string& text); void setText(const std::string& text);
wl_unique_ptr<PangoLayout> pangoLayout; wl_unique_ptr<PangoLayout> pangoLayout;
int x {0}; int x {0};
}; };
struct Tag { struct Tag {
znet_tapesoftware_dwl_wm_monitor_v1_tag_state state; znet_tapesoftware_dwl_wm_monitor_v1_tag_state state;
int numClients; int numClients;
int focusedClient; int focusedClient;
BarComponent component; BarComponent component;
}; };
struct Monitor; struct Monitor;
class Bar { class Bar {
static const zwlr_layer_surface_v1_listener _layerSurfaceListener; static const zwlr_layer_surface_v1_listener _layerSurfaceListener;
static const wl_callback_listener _frameListener; static const wl_callback_listener _frameListener;
wl_unique_ptr<wl_surface> _surface; wl_unique_ptr<wl_surface> _surface;
wl_unique_ptr<zwlr_layer_surface_v1> _layerSurface; wl_unique_ptr<zwlr_layer_surface_v1> _layerSurface;
wl_unique_ptr<PangoContext> _pangoContext; wl_unique_ptr<PangoContext> _pangoContext;
Monitor* _mon; Monitor* _mon;
std::optional<ShmBuffer> _bufs; std::optional<ShmBuffer> _bufs;
std::vector<Tag> _tags; std::vector<Tag> _tags;
BarComponent _layoutCmp, _titleCmp, _statusCmp; BarComponent _layoutCmp, _titleCmp, _statusCmp;
bool _selected; bool _selected;
bool _invalid {false}; bool _invalid {false};
// only vaild during render() // only vaild during render()
cairo_t* _painter {nullptr}; cairo_t* _painter {nullptr};
int _x; int _x;
ColorScheme _colorScheme; ColorScheme _colorScheme;
void layerSurfaceConfigure(uint32_t serial, uint32_t width, uint32_t height); void layerSurfaceConfigure(uint32_t serial, uint32_t width, uint32_t height);
void render(); void render();
void renderTags(); void renderTags();
void renderStatus(); void renderStatus();
// low-level rendering // low-level rendering
void setColorScheme(const ColorScheme& scheme, bool invert = false); void setColorScheme(const ColorScheme& scheme, bool invert = false);
void beginFg(); void beginFg();
void beginBg(); void beginBg();
void renderComponent(BarComponent& component); void renderComponent(BarComponent& component);
BarComponent createComponent(const std::string& initial = {}); BarComponent createComponent(const std::string& initial = {});
public: public:
Bar(Monitor *mon); Bar(Monitor *mon);
const wl_surface* surface() const; const wl_surface* surface() const;
bool visible() const; bool visible() const;
void show(wl_output* output); void show(wl_output* output);
void hide(); void hide();
void setTag(int tag, znet_tapesoftware_dwl_wm_monitor_v1_tag_state state, int numClients, int focusedClient); void setTag(int tag, znet_tapesoftware_dwl_wm_monitor_v1_tag_state state, int numClients, int focusedClient);
void setSelected(bool selected); void setSelected(bool selected);
void setLayout(int layout); void setLayout(int layout);
void setTitle(const std::string& title); void setTitle(const std::string& title);
void setStatus(const std::string& status); void setStatus(const std::string& status);
void invalidate(); void invalidate();
void click(int x, int y, int btn); void click(int x, int y, int btn);
}; };

View File

@ -13,12 +13,12 @@
#include "net-tapesoftware-dwl-wm-unstable-v1-client-protocol.h" #include "net-tapesoftware-dwl-wm-unstable-v1-client-protocol.h"
struct Color { struct Color {
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) { } 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}; uint8_t r, g, b, a {255};
}; };
struct ColorScheme { struct ColorScheme {
Color fg, bg; Color fg, bg;
}; };
union Arg { union Arg {
unsigned int ui; unsigned int ui;
@ -53,8 +53,8 @@ void spawn(Monitor&, const Arg& arg);
template<typename T> template<typename T>
struct wl_deleter; struct wl_deleter;
#define WL_DELETER(type, fn) template<> struct wl_deleter<type> { \ #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> template<typename T>
using wl_unique_ptr = std::unique_ptr<T, wl_deleter<T>>; using wl_unique_ptr = std::unique_ptr<T, wl_deleter<T>>;

View File

@ -17,10 +17,10 @@ constexpr ColorScheme colorActive = {Color(0xee, 0xee, 0xee), Color(0x00, 0x55,
constexpr const char* termcmd[] = {"foot", nullptr}; constexpr const char* termcmd[] = {"foot", nullptr};
constexpr Button buttons[] = { constexpr Button buttons[] = {
{ ClkTagBar, BTN_LEFT, view, {0} }, { ClkTagBar, BTN_LEFT, view, {0} },
{ ClkTagBar, BTN_RIGHT, tag, {0} }, { ClkTagBar, BTN_RIGHT, tag, {0} },
{ ClkTagBar, BTN_MIDDLE, toggletag, {0} }, { ClkTagBar, BTN_MIDDLE, toggletag, {0} },
{ ClkLayoutSymbol, BTN_LEFT, setlayout, {.ui = 0} }, { ClkLayoutSymbol, BTN_LEFT, setlayout, {.ui = 0} },
{ ClkLayoutSymbol, BTN_RIGHT, setlayout, {.ui = 2} }, { ClkLayoutSymbol, BTN_RIGHT, setlayout, {.ui = 2} },
{ ClkStatusText, BTN_RIGHT, spawn, {.v = termcmd} }, { ClkStatusText, BTN_RIGHT, spawn, {.v = termcmd} },
}; };

View File

@ -9,63 +9,63 @@
// reads data from Reader, and passes complete lines to Consumer. // reads data from Reader, and passes complete lines to Consumer.
template<size_t BufSize> template<size_t BufSize>
class LineBuffer { class LineBuffer {
using Iterator = typename std::array<char, BufSize>::iterator; using Iterator = typename std::array<char, BufSize>::iterator;
std::array<char, BufSize> _buffer; std::array<char, BufSize> _buffer;
Iterator _bufferedTo; Iterator _bufferedTo;
Iterator _consumedTo; Iterator _consumedTo;
bool _discardLine {false}; bool _discardLine {false};
public: public:
LineBuffer() LineBuffer()
: _bufferedTo {_buffer.begin()} : _bufferedTo {_buffer.begin()}
, _consumedTo {_buffer.begin()} , _consumedTo {_buffer.begin()}
{ {
} }
template<typename Reader, typename Consumer> template<typename Reader, typename Consumer>
ssize_t readLines(const Reader& reader, const Consumer& consumer) ssize_t readLines(const Reader& reader, const Consumer& consumer)
{ {
while (true) { while (true) {
auto bytesRead = reader(_bufferedTo, _buffer.end() - _bufferedTo); auto bytesRead = reader(_bufferedTo, _buffer.end() - _bufferedTo);
if (bytesRead <= 0) { if (bytesRead <= 0) {
return bytesRead; return bytesRead;
} }
_bufferedTo += bytesRead; _bufferedTo += bytesRead;
dispatchLines(consumer); dispatchLines(consumer);
resetBuffer(); resetBuffer();
} }
} }
private: private:
template<typename Consumer> template<typename Consumer>
void dispatchLines(const Consumer& consumer) void dispatchLines(const Consumer& consumer)
{ {
while (true) { while (true) {
auto separator = std::find(_consumedTo, _bufferedTo, '\n'); auto separator = std::find(_consumedTo, _bufferedTo, '\n');
if (separator == _bufferedTo) { if (separator == _bufferedTo) {
break; break;
} }
size_t lineLength = separator - _consumedTo; size_t lineLength = separator - _consumedTo;
if (!_discardLine) { if (!_discardLine) {
consumer(_consumedTo, lineLength); consumer(_consumedTo, lineLength);
} }
_consumedTo = separator + 1; _consumedTo = separator + 1;
_discardLine = false; _discardLine = false;
} }
} }
void resetBuffer() void resetBuffer()
{ {
size_t bytesRemaining = _bufferedTo - _consumedTo; size_t bytesRemaining = _bufferedTo - _consumedTo;
if (bytesRemaining == _buffer.size()) { if (bytesRemaining == _buffer.size()) {
// line too long // line too long
_discardLine = true; _discardLine = true;
_consumedTo = _buffer.begin(); _consumedTo = _buffer.begin();
_bufferedTo = _buffer.begin(); _bufferedTo = _buffer.begin();
} else if (bytesRemaining > 0 && _consumedTo > _buffer.begin()) { } else if (bytesRemaining > 0 && _consumedTo > _buffer.begin()) {
// move the last partial message to the front of the buffer, so a full-sized // move the last partial message to the front of the buffer, so a full-sized
// message will fit // message will fit
std::copy(_consumedTo, _bufferedTo, _buffer.begin()); std::copy(_consumedTo, _bufferedTo, _buffer.begin());
_consumedTo = _buffer.begin(); _consumedTo = _buffer.begin();
_bufferedTo = _consumedTo + bytesRemaining; _bufferedTo = _consumedTo + bytesRemaining;
} }
} }
}; };

View File

@ -26,26 +26,26 @@
#include "line_buffer.hpp" #include "line_buffer.hpp"
struct Monitor { struct Monitor {
uint32_t registryName; uint32_t registryName;
std::string xdgName; std::string xdgName;
wl_unique_ptr<wl_output> wlOutput; wl_unique_ptr<wl_output> wlOutput;
wl_unique_ptr<znet_tapesoftware_dwl_wm_monitor_v1> dwlMonitor; wl_unique_ptr<znet_tapesoftware_dwl_wm_monitor_v1> dwlMonitor;
std::optional<Bar> bar; std::optional<Bar> bar;
bool desiredVisibility {true}; bool desiredVisibility {true};
bool hasData; bool hasData;
uint32_t tags; uint32_t tags;
}; };
struct SeatPointer { struct SeatPointer {
wl_unique_ptr<wl_pointer> wlPointer; wl_unique_ptr<wl_pointer> wlPointer;
Bar* focusedBar; Bar* focusedBar;
int x, y; int x, y;
std::vector<int> btns; std::vector<int> btns;
}; };
struct Seat { struct Seat {
uint32_t name; uint32_t name;
wl_unique_ptr<wl_seat> wlSeat; wl_unique_ptr<wl_seat> wlSeat;
std::optional<SeatPointer> pointer; std::optional<SeatPointer> pointer;
}; };
static void updatemon(Monitor &mon); static void updatemon(Monitor &mon);
@ -81,238 +81,238 @@ static bool quitting {false};
void view(Monitor& m, const Arg& arg) 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) 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) 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) 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) 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) void spawn(Monitor&, const Arg& arg)
{ {
if (fork() == 0) { if (fork() == 0) {
auto argv = static_cast<char* const*>(arg.v); auto argv = static_cast<char* const*>(arg.v);
setsid(); setsid();
execvp(argv[0], argv); execvp(argv[0], argv);
fprintf(stderr, "somebar: execvp %s ", argv[0]); fprintf(stderr, "somebar: execvp %s ", argv[0]);
perror(" failed"); perror(" failed");
exit(1); exit(1);
} }
} }
static const struct xdg_wm_base_listener xdgWmBaseListener = { static const struct xdg_wm_base_listener xdgWmBaseListener = {
[](void*, xdg_wm_base* sender, uint32_t serial) { [](void*, xdg_wm_base* sender, uint32_t serial) {
xdg_wm_base_pong(sender, serial); xdg_wm_base_pong(sender, serial);
} }
}; };
static const struct zxdg_output_v1_listener xdgOutputListener = { static const struct zxdg_output_v1_listener xdgOutputListener = {
.logical_position = [](void*, zxdg_output_v1*, int, int) { }, .logical_position = [](void*, zxdg_output_v1*, int, int) { },
.logical_size = [](void*, zxdg_output_v1*, int, int) { }, .logical_size = [](void*, zxdg_output_v1*, int, int) { },
.done = [](void*, zxdg_output_v1*) { }, .done = [](void*, zxdg_output_v1*) { },
.name = [](void* mp, zxdg_output_v1* xdgOutput, const char* name) { .name = [](void* mp, zxdg_output_v1* xdgOutput, const char* name) {
auto& monitor = *static_cast<Monitor*>(mp); auto& monitor = *static_cast<Monitor*>(mp);
monitor.xdgName = name; monitor.xdgName = name;
zxdg_output_v1_destroy(xdgOutput); zxdg_output_v1_destroy(xdgOutput);
}, },
.description = [](void*, zxdg_output_v1*, const char*) { }, .description = [](void*, zxdg_output_v1*, const char*) { },
}; };
static Bar* barFromSurface(const wl_surface *surface) static Bar* barFromSurface(const wl_surface *surface)
{ {
auto mon = std::find_if(begin(monitors), end(monitors), [surface](const Monitor& mon) { auto mon = std::find_if(begin(monitors), end(monitors), [surface](const Monitor& mon) {
return mon.bar && mon.bar->surface() == surface; return mon.bar && mon.bar->surface() == surface;
}); });
return mon != end(monitors) && mon->bar ? &*mon->bar : nullptr; return mon != end(monitors) && mon->bar ? &*mon->bar : nullptr;
} }
static const struct wl_pointer_listener pointerListener = { static const struct wl_pointer_listener pointerListener = {
.enter = [](void* sp, wl_pointer* pointer, uint32_t serial, .enter = [](void* sp, wl_pointer* pointer, uint32_t serial,
wl_surface* surface, wl_fixed_t x, wl_fixed_t y) wl_surface* surface, wl_fixed_t x, wl_fixed_t y)
{ {
auto& seat = *static_cast<Seat*>(sp); auto& seat = *static_cast<Seat*>(sp);
seat.pointer->focusedBar = barFromSurface(surface); seat.pointer->focusedBar = barFromSurface(surface);
if (!cursorImage) { if (!cursorImage) {
auto cursorTheme = wl_cursor_theme_load(nullptr, 24, shm); auto cursorTheme = wl_cursor_theme_load(nullptr, 24, shm);
cursorImage = wl_cursor_theme_get_cursor(cursorTheme, "left_ptr")->images[0]; cursorImage = wl_cursor_theme_get_cursor(cursorTheme, "left_ptr")->images[0];
cursorSurface = wl_compositor_create_surface(compositor); cursorSurface = wl_compositor_create_surface(compositor);
wl_surface_attach(cursorSurface, wl_cursor_image_get_buffer(cursorImage), 0, 0); wl_surface_attach(cursorSurface, wl_cursor_image_get_buffer(cursorImage), 0, 0);
wl_surface_commit(cursorSurface); wl_surface_commit(cursorSurface);
} }
wl_pointer_set_cursor(pointer, serial, cursorSurface, wl_pointer_set_cursor(pointer, serial, cursorSurface,
cursorImage->hotspot_x, cursorImage->hotspot_y); cursorImage->hotspot_x, cursorImage->hotspot_y);
}, },
.leave = [](void* sp, wl_pointer*, uint32_t serial, wl_surface*) { .leave = [](void* sp, wl_pointer*, uint32_t serial, wl_surface*) {
auto& seat = *static_cast<Seat*>(sp); auto& seat = *static_cast<Seat*>(sp);
seat.pointer->focusedBar = nullptr; seat.pointer->focusedBar = nullptr;
}, },
.motion = [](void* sp, wl_pointer*, uint32_t, wl_fixed_t x, wl_fixed_t y) { .motion = [](void* sp, wl_pointer*, uint32_t, wl_fixed_t x, wl_fixed_t y) {
auto& seat = *static_cast<Seat*>(sp); auto& seat = *static_cast<Seat*>(sp);
seat.pointer->x = wl_fixed_to_int(x); seat.pointer->x = wl_fixed_to_int(x);
seat.pointer->y = wl_fixed_to_int(y); seat.pointer->y = wl_fixed_to_int(y);
}, },
.button = [](void* sp, wl_pointer*, uint32_t, uint32_t, uint32_t button, uint32_t pressed) { .button = [](void* sp, wl_pointer*, uint32_t, uint32_t, uint32_t button, uint32_t pressed) {
auto& seat = *static_cast<Seat*>(sp); auto& seat = *static_cast<Seat*>(sp);
auto it = std::find(begin(seat.pointer->btns), end(seat.pointer->btns), button); 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)) { if (pressed == WL_POINTER_BUTTON_STATE_PRESSED && it == end(seat.pointer->btns)) {
seat.pointer->btns.push_back(button); seat.pointer->btns.push_back(button);
} else if (pressed == WL_POINTER_BUTTON_STATE_RELEASED && it != end(seat.pointer->btns)) { } else if (pressed == WL_POINTER_BUTTON_STATE_RELEASED && it != end(seat.pointer->btns)) {
seat.pointer->btns.erase(it); seat.pointer->btns.erase(it);
} }
}, },
.axis = [](void* sp, wl_pointer*, uint32_t, uint32_t, wl_fixed_t) { }, .axis = [](void* sp, wl_pointer*, uint32_t, uint32_t, wl_fixed_t) { },
.frame = [](void* sp, wl_pointer*) { .frame = [](void* sp, wl_pointer*) {
auto& seat = *static_cast<Seat*>(sp); auto& seat = *static_cast<Seat*>(sp);
if (!seat.pointer->focusedBar) return; if (!seat.pointer->focusedBar) return;
for (auto btn : seat.pointer->btns) { for (auto btn : seat.pointer->btns) {
seat.pointer->focusedBar->click(seat.pointer->x, seat.pointer->y, btn); seat.pointer->focusedBar->click(seat.pointer->x, seat.pointer->y, btn);
} }
seat.pointer->btns.clear(); seat.pointer->btns.clear();
}, },
.axis_source = [](void*, wl_pointer*, uint32_t) { }, .axis_source = [](void*, wl_pointer*, uint32_t) { },
.axis_stop = [](void*, wl_pointer*, uint32_t, uint32_t) { }, .axis_stop = [](void*, wl_pointer*, uint32_t, uint32_t) { },
.axis_discrete = [](void*, wl_pointer*, uint32_t, int32_t) { }, .axis_discrete = [](void*, wl_pointer*, uint32_t, int32_t) { },
}; };
static const struct wl_seat_listener seatListener = { static const struct wl_seat_listener seatListener = {
.capabilities = [](void* sp, wl_seat*, uint32_t cap) .capabilities = [](void* sp, wl_seat*, uint32_t cap)
{ {
auto& seat = *static_cast<Seat*>(sp); auto& seat = *static_cast<Seat*>(sp);
auto hasPointer = cap & WL_SEAT_CAPABILITY_POINTER; auto hasPointer = cap & WL_SEAT_CAPABILITY_POINTER;
if (!seat.pointer && hasPointer) { if (!seat.pointer && hasPointer) {
auto &pointer = seat.pointer.emplace(); auto &pointer = seat.pointer.emplace();
pointer.wlPointer = wl_unique_ptr<wl_pointer> {wl_seat_get_pointer(seat.wlSeat.get())}; pointer.wlPointer = wl_unique_ptr<wl_pointer> {wl_seat_get_pointer(seat.wlSeat.get())};
wl_pointer_add_listener(seat.pointer->wlPointer.get(), &pointerListener, &seat); wl_pointer_add_listener(seat.pointer->wlPointer.get(), &pointerListener, &seat);
} else if (seat.pointer && !hasPointer) { } else if (seat.pointer && !hasPointer) {
seat.pointer.reset(); seat.pointer.reset();
} }
}, },
.name = [](void*, wl_seat*, const char *name) { } .name = [](void*, wl_seat*, const char *name) { }
}; };
static const struct znet_tapesoftware_dwl_wm_v1_listener dwlWmListener = { static const struct znet_tapesoftware_dwl_wm_v1_listener dwlWmListener = {
.tag = [](void*, znet_tapesoftware_dwl_wm_v1*, const char* name) { .tag = [](void*, znet_tapesoftware_dwl_wm_v1*, const char* name) {
tagNames.push_back(name); tagNames.push_back(name);
}, },
.layout = [](void*, znet_tapesoftware_dwl_wm_v1*, const char* name) { .layout = [](void*, znet_tapesoftware_dwl_wm_v1*, const char* name) {
layoutNames.push_back(name); layoutNames.push_back(name);
}, },
}; };
static const struct znet_tapesoftware_dwl_wm_monitor_v1_listener dwlWmMonitorListener { static const struct znet_tapesoftware_dwl_wm_monitor_v1_listener dwlWmMonitorListener {
.selected = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*, uint32_t selected) { .selected = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*, uint32_t selected) {
auto mon = static_cast<Monitor*>(mv); auto mon = static_cast<Monitor*>(mv);
if (selected) { if (selected) {
selmon = mon; selmon = mon;
} else if (selmon == mon) { } else if (selmon == mon) {
selmon = nullptr; selmon = nullptr;
} }
mon->bar->setSelected(selected); 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) { .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); auto mon = static_cast<Monitor*>(mv);
mon->bar->setTag(tag, static_cast<znet_tapesoftware_dwl_wm_monitor_v1_tag_state>(state), numClients, focusedClient); mon->bar->setTag(tag, static_cast<znet_tapesoftware_dwl_wm_monitor_v1_tag_state>(state), numClients, focusedClient);
uint32_t mask = 1 << tag; uint32_t mask = 1 << tag;
if (state & ZNET_TAPESOFTWARE_DWL_WM_MONITOR_V1_TAG_STATE_ACTIVE) { if (state & ZNET_TAPESOFTWARE_DWL_WM_MONITOR_V1_TAG_STATE_ACTIVE) {
mon->tags |= mask; mon->tags |= mask;
} else { } else {
mon->tags &= ~mask; mon->tags &= ~mask;
} }
}, },
.layout = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*, uint32_t layout) { .layout = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*, uint32_t layout) {
auto mon = static_cast<Monitor*>(mv); auto mon = static_cast<Monitor*>(mv);
mon->bar->setLayout(layout); mon->bar->setLayout(layout);
}, },
.title = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*, const char* title) { .title = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*, const char* title) {
auto mon = static_cast<Monitor*>(mv); auto mon = static_cast<Monitor*>(mv);
mon->bar->setTitle(title); mon->bar->setTitle(title);
}, },
.frame = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*) { .frame = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*) {
auto mon = static_cast<Monitor*>(mv); auto mon = static_cast<Monitor*>(mv);
mon->hasData = true; mon->hasData = true;
updatemon(*mon); updatemon(*mon);
} }
}; };
static void setupMonitor(Monitor& monitor) { static void setupMonitor(Monitor& monitor) {
monitor.dwlMonitor.reset(znet_tapesoftware_dwl_wm_v1_get_monitor(dwlWm, monitor.wlOutput.get())); monitor.dwlMonitor.reset(znet_tapesoftware_dwl_wm_v1_get_monitor(dwlWm, monitor.wlOutput.get()));
monitor.bar.emplace(&monitor); monitor.bar.emplace(&monitor);
monitor.bar->setStatus(lastStatus); monitor.bar->setStatus(lastStatus);
auto xdgOutput = zxdg_output_manager_v1_get_xdg_output(xdgOutputManager, monitor.wlOutput.get()); auto xdgOutput = zxdg_output_manager_v1_get_xdg_output(xdgOutputManager, monitor.wlOutput.get());
zxdg_output_v1_add_listener(xdgOutput, &xdgOutputListener, &monitor); zxdg_output_v1_add_listener(xdgOutput, &xdgOutputListener, &monitor);
znet_tapesoftware_dwl_wm_monitor_v1_add_listener(monitor.dwlMonitor.get(), &dwlWmMonitorListener, &monitor); znet_tapesoftware_dwl_wm_monitor_v1_add_listener(monitor.dwlMonitor.get(), &dwlWmMonitorListener, &monitor);
} }
static void updatemon(Monitor& mon) static void updatemon(Monitor& mon)
{ {
if (!mon.hasData) return; if (!mon.hasData) return;
if (mon.desiredVisibility) { if (mon.desiredVisibility) {
if (mon.bar->visible()) { if (mon.bar->visible()) {
mon.bar->invalidate(); mon.bar->invalidate();
} else { } else {
mon.bar->show(mon.wlOutput.get()); mon.bar->show(mon.wlOutput.get());
} }
} else if (mon.bar->visible()) { } else if (mon.bar->visible()) {
mon.bar->hide(); mon.bar->hide();
} }
} }
// called after we have received the initial batch of globals // called after we have received the initial batch of globals
static void onReady() static void onReady()
{ {
requireGlobal(compositor, "wl_compositor"); requireGlobal(compositor, "wl_compositor");
requireGlobal(shm, "wl_shm"); requireGlobal(shm, "wl_shm");
requireGlobal(wlrLayerShell, "zwlr_layer_shell_v1"); requireGlobal(wlrLayerShell, "zwlr_layer_shell_v1");
requireGlobal(xdgOutputManager, "zxdg_output_manager_v1"); requireGlobal(xdgOutputManager, "zxdg_output_manager_v1");
requireGlobal(dwlWm, "znet_tapesoftware_dwl_wm_v1"); requireGlobal(dwlWm, "znet_tapesoftware_dwl_wm_v1");
setupStatusFifo(); setupStatusFifo();
wl_display_roundtrip(display); // roundtrip so we receive all dwl tags etc. wl_display_roundtrip(display); // roundtrip so we receive all dwl tags etc.
ready = true; ready = true;
for (auto& monitor : monitors) { for (auto& monitor : monitors) {
setupMonitor(monitor); setupMonitor(monitor);
} }
} }
static void setupStatusFifo() static void setupStatusFifo()
{ {
for (auto i=0; i<100; i++) { for (auto i=0; i<100; i++) {
auto path = std::string{getenv("XDG_RUNTIME_DIR")} + "/somebar-" + std::to_string(i); auto path = std::string{getenv("XDG_RUNTIME_DIR")} + "/somebar-" + std::to_string(i);
auto result = mkfifo(path.c_str(), 0666); auto result = mkfifo(path.c_str(), 0666);
if (result == 0) { if (result == 0) {
auto fd = open(path.c_str(), O_CLOEXEC | O_NONBLOCK | O_RDONLY); auto fd = open(path.c_str(), O_CLOEXEC | O_NONBLOCK | O_RDONLY);
if (fd < 0) { if (fd < 0) {
diesys("open status fifo reader"); diesys("open status fifo reader");
} }
statusFifoName = path; statusFifoName = path;
statusFifoFd = fd; statusFifoFd = fd;
fd = open(path.c_str(), O_CLOEXEC | O_WRONLY); fd = open(path.c_str(), O_CLOEXEC | O_WRONLY);
if (fd < 0) { if (fd < 0) {
diesys("open status fifo writer"); diesys("open status fifo writer");
} }
statusFifoWriter = fd; statusFifoWriter = fd;
epoll_event ev = {0}; epoll_event ev = {0};
ev.events = EPOLLIN; ev.events = EPOLLIN;
ev.data.fd = statusFifoFd; ev.data.fd = statusFifoFd;
if (epoll_ctl(epoll, EPOLL_CTL_ADD, statusFifoFd, &ev) < 0) { if (epoll_ctl(epoll, EPOLL_CTL_ADD, statusFifoFd, &ev) < 0) {
diesys("epoll_ctl add status fifo"); diesys("epoll_ctl add status fifo");
} }
return; return;
} else if (errno != EEXIST) { } else if (errno != EEXIST) {
diesys("mkfifo"); diesys("mkfifo");
} }
} }
} }
const std::string prefixStatus = "status "; const std::string prefixStatus = "status ";
@ -325,243 +325,243 @@ const std::string argSelected = "selected";
template<typename T> template<typename T>
static void updateVisibility(const std::string& name, T updater) static void updateVisibility(const std::string& name, T updater)
{ {
auto isCurrent = name == argSelected; auto isCurrent = name == argSelected;
auto isAll = name == argAll; auto isAll = name == argAll;
for (auto& mon : monitors) { for (auto& mon : monitors) {
if (isAll || if (isAll ||
isCurrent && &mon == selmon || isCurrent && &mon == selmon ||
mon.xdgName == name) { mon.xdgName == name) {
auto newVisibility = updater(mon.desiredVisibility); auto newVisibility = updater(mon.desiredVisibility);
if (newVisibility != mon.desiredVisibility) { if (newVisibility != mon.desiredVisibility) {
mon.desiredVisibility = newVisibility; mon.desiredVisibility = newVisibility;
updatemon(mon); updatemon(mon);
} }
} }
} }
} }
static LineBuffer<512> _statusBuffer; static LineBuffer<512> _statusBuffer;
static void onStatus() static void onStatus()
{ {
_statusBuffer.readLines( _statusBuffer.readLines(
[](void* p, size_t size) { [](void* p, size_t size) {
return read(statusFifoFd, p, size); return read(statusFifoFd, p, size);
}, },
[](const char* buffer, size_t n) { [](const char* buffer, size_t n) {
auto str = std::string {buffer, n}; auto str = std::string {buffer, n};
if (str.rfind(prefixStatus, 0) == 0) { if (str.rfind(prefixStatus, 0) == 0) {
lastStatus = str.substr(prefixStatus.size()); lastStatus = str.substr(prefixStatus.size());
for (auto &monitor : monitors) { for (auto &monitor : monitors) {
if (monitor.bar) { if (monitor.bar) {
monitor.bar->setStatus(lastStatus); monitor.bar->setStatus(lastStatus);
monitor.bar->invalidate(); monitor.bar->invalidate();
} }
} }
} else if (str.rfind(prefixShow, 0) == 0) { } else if (str.rfind(prefixShow, 0) == 0) {
updateVisibility(str.substr(prefixShow.size()), [](bool) { return true; }); updateVisibility(str.substr(prefixShow.size()), [](bool) { return true; });
} else if (str.rfind(prefixHide, 0) == 0) { } else if (str.rfind(prefixHide, 0) == 0) {
updateVisibility(str.substr(prefixHide.size()), [](bool) { return false; }); updateVisibility(str.substr(prefixHide.size()), [](bool) { return false; });
} else if (str.rfind(prefixToggle, 0) == 0) { } else if (str.rfind(prefixToggle, 0) == 0) {
updateVisibility(str.substr(prefixToggle.size()), [](bool vis) { return !vis; }); updateVisibility(str.substr(prefixToggle.size()), [](bool vis) { return !vis; });
} }
}); });
} }
struct HandleGlobalHelper { struct HandleGlobalHelper {
wl_registry* registry; wl_registry* registry;
uint32_t name; uint32_t name;
const char* interface; const char* interface;
template<typename T> template<typename T>
bool handle(T& store, const wl_interface& iface, int version) { bool handle(T& store, const wl_interface& iface, int version) {
if (strcmp(interface, iface.name)) return false; if (strcmp(interface, iface.name)) return false;
store = static_cast<T>(wl_registry_bind(registry, name, &iface, version)); store = static_cast<T>(wl_registry_bind(registry, name, &iface, version));
return true; return true;
} }
}; };
static void registryHandleGlobal(void*, wl_registry* registry, uint32_t name, const char* interface, uint32_t version) static void registryHandleGlobal(void*, wl_registry* registry, uint32_t name, const char* interface, uint32_t version)
{ {
auto reg = HandleGlobalHelper { registry, name, interface }; auto reg = HandleGlobalHelper { registry, name, interface };
if (reg.handle(compositor, wl_compositor_interface, 4)) return; if (reg.handle(compositor, wl_compositor_interface, 4)) return;
if (reg.handle(shm, wl_shm_interface, 1)) return; if (reg.handle(shm, wl_shm_interface, 1)) return;
if (reg.handle(wlrLayerShell, zwlr_layer_shell_v1_interface, 4)) 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(xdgOutputManager, zxdg_output_manager_v1_interface, 3)) return;
if (reg.handle(xdgWmBase, xdg_wm_base_interface, 2)) { if (reg.handle(xdgWmBase, xdg_wm_base_interface, 2)) {
xdg_wm_base_add_listener(xdgWmBase, &xdgWmBaseListener, nullptr); xdg_wm_base_add_listener(xdgWmBase, &xdgWmBaseListener, nullptr);
return; return;
} }
if (reg.handle(dwlWm, znet_tapesoftware_dwl_wm_v1_interface, 1)) { if (reg.handle(dwlWm, znet_tapesoftware_dwl_wm_v1_interface, 1)) {
znet_tapesoftware_dwl_wm_v1_add_listener(dwlWm, &dwlWmListener, nullptr); znet_tapesoftware_dwl_wm_v1_add_listener(dwlWm, &dwlWmListener, nullptr);
return; return;
} }
if (wl_seat *wlSeat; reg.handle(wlSeat, wl_seat_interface, 7)) { if (wl_seat *wlSeat; reg.handle(wlSeat, wl_seat_interface, 7)) {
auto& seat = seats.emplace_back(Seat {name, wl_unique_ptr<wl_seat> {wlSeat}}); auto& seat = seats.emplace_back(Seat {name, wl_unique_ptr<wl_seat> {wlSeat}});
wl_seat_add_listener(wlSeat, &seatListener, &seat); wl_seat_add_listener(wlSeat, &seatListener, &seat);
return; return;
} }
if (wl_output *output; reg.handle(output, wl_output_interface, 1)) { if (wl_output *output; reg.handle(output, wl_output_interface, 1)) {
auto& m = monitors.emplace_back(Monitor {name, {}, wl_unique_ptr<wl_output> {output}}); auto& m = monitors.emplace_back(Monitor {name, {}, wl_unique_ptr<wl_output> {output}});
if (ready) { if (ready) {
setupMonitor(m); setupMonitor(m);
} }
return; return;
} }
} }
static void registryHandleRemove(void*, wl_registry* registry, uint32_t name) static void registryHandleRemove(void*, wl_registry* registry, uint32_t name)
{ {
monitors.remove_if([name](const Monitor &mon) { return mon.registryName == name; }); monitors.remove_if([name](const Monitor &mon) { return mon.registryName == name; });
seats.remove_if([name](const Seat &seat) { return seat.name == name; }); seats.remove_if([name](const Seat &seat) { return seat.name == name; });
} }
static const struct wl_registry_listener registry_listener = { static const struct wl_registry_listener registry_listener = {
.global = registryHandleGlobal, .global = registryHandleGlobal,
.global_remove = registryHandleRemove, .global_remove = registryHandleRemove,
}; };
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
int opt; int opt;
while ((opt = getopt(argc, argv, "chv")) != -1) { while ((opt = getopt(argc, argv, "chv")) != -1) {
switch (opt) { switch (opt) {
case 'h': case 'h':
printf("Usage: %s [-h] [-v] [-c command]\n", argv[0]); printf("Usage: %s [-h] [-v] [-c command]\n", argv[0]);
printf(" -h: Show this help\n"); printf(" -h: Show this help\n");
printf(" -v: Show somebar version\n"); printf(" -v: Show somebar version\n");
printf(" -c: Sends a command to sombar. See README for details.\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("If any of these are specified, somebar exits after the action.\n");
printf("Otherwise, somebar will display itself.\n"); printf("Otherwise, somebar will display itself.\n");
exit(0); exit(0);
case 'v': case 'v':
printf("somebar " SOMEBAR_VERSION "\n"); printf("somebar " SOMEBAR_VERSION "\n");
exit(0); exit(0);
case 'c': case 'c':
if (optind >= argc) { if (optind >= argc) {
die("Expected command"); die("Expected command");
} }
auto path = std::string {getenv("XDG_RUNTIME_DIR")} + "/somebar-0"; auto path = std::string {getenv("XDG_RUNTIME_DIR")} + "/somebar-0";
int fd = open(path.c_str(), O_WRONLY | O_CLOEXEC); int fd = open(path.c_str(), O_WRONLY | O_CLOEXEC);
if (fd < 0) { if (fd < 0) {
fprintf(stderr, "could not open %s: ", path.c_str()); fprintf(stderr, "could not open %s: ", path.c_str());
perror(""); perror("");
exit(1); exit(1);
} }
auto str = std::string {}; auto str = std::string {};
for (auto i = optind; i<argc; i++) { for (auto i = optind; i<argc; i++) {
if (i > optind) str += " "; if (i > optind) str += " ";
str += argv[i]; str += argv[i];
} }
str += "\n"; str += "\n";
write(fd, str.c_str(), str.size()); write(fd, str.c_str(), str.size());
exit(0); exit(0);
} }
} }
static sigset_t blockedsigs; static sigset_t blockedsigs;
sigemptyset(&blockedsigs); sigemptyset(&blockedsigs);
sigaddset(&blockedsigs, SIGINT); sigaddset(&blockedsigs, SIGINT);
sigaddset(&blockedsigs, SIGTERM); sigaddset(&blockedsigs, SIGTERM);
sigprocmask(SIG_BLOCK, &blockedsigs, nullptr); sigprocmask(SIG_BLOCK, &blockedsigs, nullptr);
epoll_event epollEv = {0}; epoll_event epollEv = {0};
std::array<epoll_event, 5> epollEvents; std::array<epoll_event, 5> epollEvents;
epoll = epoll_create1(EPOLL_CLOEXEC); epoll = epoll_create1(EPOLL_CLOEXEC);
if (epoll < 0) { if (epoll < 0) {
diesys("epoll_create1"); diesys("epoll_create1");
} }
int sfd = signalfd(-1, &blockedsigs, SFD_CLOEXEC | SFD_NONBLOCK); int sfd = signalfd(-1, &blockedsigs, SFD_CLOEXEC | SFD_NONBLOCK);
if (sfd < 0) { if (sfd < 0) {
diesys("signalfd"); diesys("signalfd");
} }
epollEv.events = EPOLLIN; epollEv.events = EPOLLIN;
epollEv.data.fd = sfd; epollEv.data.fd = sfd;
if (epoll_ctl(epoll, EPOLL_CTL_ADD, sfd, &epollEv) < 0) { if (epoll_ctl(epoll, EPOLL_CTL_ADD, sfd, &epollEv) < 0) {
diesys("epoll_ctl add signalfd"); diesys("epoll_ctl add signalfd");
} }
display = wl_display_connect(nullptr); display = wl_display_connect(nullptr);
if (!display) { if (!display) {
die("Failed to connect to Wayland display"); die("Failed to connect to Wayland display");
} }
displayFd = wl_display_get_fd(display); displayFd = wl_display_get_fd(display);
auto registry = wl_display_get_registry(display); auto registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, nullptr); wl_registry_add_listener(registry, &registry_listener, nullptr);
wl_display_roundtrip(display); wl_display_roundtrip(display);
onReady(); onReady();
epollEv.events = EPOLLIN; epollEv.events = EPOLLIN;
epollEv.data.fd = displayFd; epollEv.data.fd = displayFd;
if (epoll_ctl(epoll, EPOLL_CTL_ADD, displayFd, &epollEv) < 0) { if (epoll_ctl(epoll, EPOLL_CTL_ADD, displayFd, &epollEv) < 0) {
diesys("epoll_ctl add wayland_display"); diesys("epoll_ctl add wayland_display");
} }
while (!quitting) { while (!quitting) {
waylandFlush(); waylandFlush();
auto res = epoll_wait(epoll, epollEvents.data(), epollEvents.size(), -1); auto res = epoll_wait(epoll, epollEvents.data(), epollEvents.size(), -1);
if (res < 0) { if (res < 0) {
if (errno != EINTR) { if (errno != EINTR) {
diesys("epoll_wait"); diesys("epoll_wait");
} }
} else { } else {
for (auto i=0; i<res; i++) { for (auto i=0; i<res; i++) {
auto &ev = epollEvents[i]; auto &ev = epollEvents[i];
if (ev.data.fd == displayFd) { if (ev.data.fd == displayFd) {
if (ev.events & EPOLLIN) { if (ev.events & EPOLLIN) {
if (wl_display_dispatch(display) < 0) { if (wl_display_dispatch(display) < 0) {
die("wl_display_dispatch"); die("wl_display_dispatch");
} }
} if (ev.events & EPOLLOUT) { } if (ev.events & EPOLLOUT) {
epollEv.events = EPOLLIN; epollEv.events = EPOLLIN;
epollEv.data.fd = displayFd; epollEv.data.fd = displayFd;
if (epoll_ctl(epoll, EPOLL_CTL_MOD, displayFd, &epollEv) < 0) { if (epoll_ctl(epoll, EPOLL_CTL_MOD, displayFd, &epollEv) < 0) {
diesys("epoll_ctl"); diesys("epoll_ctl");
} }
waylandFlush(); waylandFlush();
} }
} else if (ev.data.fd == statusFifoFd) { } else if (ev.data.fd == statusFifoFd) {
onStatus(); onStatus();
} else if (ev.data.fd == sfd) { } else if (ev.data.fd == sfd) {
quitting = true; quitting = true;
} }
} }
} }
} }
cleanup(); cleanup();
} }
void requireGlobal(const void *p, const char *name) void requireGlobal(const void *p, const char *name)
{ {
if (p) return; if (p) return;
fprintf(stderr, "Wayland compositor does not export required global %s, aborting.\n", name); fprintf(stderr, "Wayland compositor does not export required global %s, aborting.\n", name);
cleanup(); cleanup();
exit(1); exit(1);
} }
void waylandFlush() void waylandFlush()
{ {
wl_display_dispatch_pending(display); wl_display_dispatch_pending(display);
if (wl_display_flush(display) < 0 && errno == EAGAIN) { if (wl_display_flush(display) < 0 && errno == EAGAIN) {
epoll_event ev = {0}; epoll_event ev = {0};
ev.events = EPOLLIN | EPOLLOUT; ev.events = EPOLLIN | EPOLLOUT;
ev.data.fd = displayFd; ev.data.fd = displayFd;
if (epoll_ctl(epoll, EPOLL_CTL_MOD, displayFd, &ev) < 0) { if (epoll_ctl(epoll, EPOLL_CTL_MOD, displayFd, &ev) < 0) {
diesys("epoll_ctl"); diesys("epoll_ctl");
} }
} }
} }
void die(const char* why) { void die(const char* why) {
fprintf(stderr, "%s\n", why); fprintf(stderr, "%s\n", why);
cleanup(); cleanup();
exit(1); exit(1);
} }
void diesys(const char* why) { void diesys(const char* why) {
perror(why); perror(why);
cleanup(); cleanup();
exit(1); exit(1);
} }
void cleanup() { void cleanup() {
if (!statusFifoName.empty()) { if (!statusFifoName.empty()) {
unlink(statusFifoName.c_str()); unlink(statusFifoName.c_str());
} }
} }

View File

@ -9,26 +9,26 @@
constexpr int n = 2; constexpr int n = 2;
ShmBuffer::ShmBuffer(int w, int h, wl_shm_format format) ShmBuffer::ShmBuffer(int w, int h, wl_shm_format format)
: width(w) : width(w)
, height(h) , height(h)
, stride(w*4) , stride(w*4)
{ {
auto oneSize = stride*size_t(h); auto oneSize = stride*size_t(h);
auto totalSize = oneSize * n; auto totalSize = oneSize * n;
auto fd = memfd_create("wl_shm", MFD_CLOEXEC); auto fd = memfd_create("wl_shm", MFD_CLOEXEC);
ftruncate(fd, totalSize); ftruncate(fd, totalSize);
auto pool = wl_shm_create_pool(shm, 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)); auto ptr = reinterpret_cast<uint8_t*>(mmap(nullptr, totalSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
_mapping = MemoryMapping {ptr, totalSize}; _mapping = MemoryMapping {ptr, totalSize};
close(fd); close(fd);
for (auto i=0; i<n; i++) { for (auto i=0; i<n; i++) {
auto offset = oneSize*i; auto offset = oneSize*i;
_buffers[i] = { _buffers[i] = {
ptr+offset, ptr+offset,
wl_unique_ptr<wl_buffer> { wl_shm_pool_create_buffer(pool, offset, width, height, stride, format) }, wl_unique_ptr<wl_buffer> { wl_shm_pool_create_buffer(pool, offset, width, height, stride, format) },
}; };
} }
wl_shm_pool_destroy(pool); wl_shm_pool_destroy(pool);
} }
uint8_t* ShmBuffer::data() { return _buffers[_current].data; } uint8_t* ShmBuffer::data() { return _buffers[_current].data; }

View File

@ -8,38 +8,38 @@
#include "common.hpp" #include "common.hpp"
class MemoryMapping { class MemoryMapping {
void* _ptr {nullptr}; void* _ptr {nullptr};
size_t _size {0}; size_t _size {0};
public: public:
MemoryMapping() { } MemoryMapping() { }
explicit MemoryMapping(void* ptr, size_t size) : _ptr(ptr), _size(size) { } explicit MemoryMapping(void* ptr, size_t size) : _ptr(ptr), _size(size) { }
MemoryMapping(const MemoryMapping&) = delete; MemoryMapping(const MemoryMapping&) = delete;
MemoryMapping(MemoryMapping&& other) { swap(other); } MemoryMapping(MemoryMapping&& other) { swap(other); }
MemoryMapping& operator=(const MemoryMapping& other) = delete; MemoryMapping& operator=(const MemoryMapping& other) = delete;
MemoryMapping& operator=(MemoryMapping&& other) { swap(other); return *this; } MemoryMapping& operator=(MemoryMapping&& other) { swap(other); return *this; }
~MemoryMapping() { if (_ptr) munmap(_ptr, _size); } ~MemoryMapping() { if (_ptr) munmap(_ptr, _size); }
void swap(MemoryMapping &other) { void swap(MemoryMapping &other) {
using std::swap; using std::swap;
swap(_ptr, other._ptr); swap(_ptr, other._ptr);
swap(_size, other._size); swap(_size, other._size);
} }
}; };
// double buffered shm // double buffered shm
// format is must be 32-bit // format is must be 32-bit
class ShmBuffer { class ShmBuffer {
struct Buf { struct Buf {
uint8_t* data {nullptr}; uint8_t* data {nullptr};
wl_unique_ptr<wl_buffer> buffer; wl_unique_ptr<wl_buffer> buffer;
}; };
std::array<Buf, 2> _buffers; std::array<Buf, 2> _buffers;
int _current {0}; int _current {0};
MemoryMapping _mapping; MemoryMapping _mapping;
public: public:
int width, height, stride; int width, height, stride;
explicit ShmBuffer(int width, int height, wl_shm_format format); explicit ShmBuffer(int width, int height, wl_shm_format format);
uint8_t* data(); uint8_t* data();
wl_buffer* buffer(); wl_buffer* buffer();
void flip(); void flip();
}; };