diff --git a/protocols/dwl-ipc-unstable-v2.xml b/protocols/dwl-ipc-unstable-v2.xml new file mode 100644 index 0000000..0a6e7e5 --- /dev/null +++ b/protocols/dwl-ipc-unstable-v2.xml @@ -0,0 +1,181 @@ + + + + + This protocol allows clients to update and get updates from dwl. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible + changes may be added together with the corresponding interface + version bump. + Backward incompatible changes are done by bumping the version + number in the protocol and interface names and resetting the + interface version. Once the protocol is to be declared stable, + the 'z' prefix and the version number in the protocol and + interface names are removed and the interface version number is + reset. + + + + + This interface is exposed as a global in wl_registry. + + Clients can use this interface to get a dwl_ipc_output. + After binding the client will recieve the dwl_ipc_manager.tags and dwl_ipc_manager.layout events. + The dwl_ipc_manager.tags and dwl_ipc_manager.layout events expose tags and layouts to the client. + + + + + Indicates that the client will not the dwl_ipc_manager object anymore. + Objects created through this instance are not affected. + + + + + + Get a dwl_ipc_outout for the specified wl_output. + + + + + + + + This event is sent after binding. + A roundtrip after binding guarantees the client recieved all tags. + + + + + + + This event is sent after binding. + A roundtrip after binding guarantees the client recieved all layouts. + + + + + + + + Observe and control a dwl output. + + Events are double-buffered: + Clients should cache events and redraw when a dwl_ipc_output.frame event is sent. + + Request are not double-buffered: + The compositor will update immediately upon request. + + + + + + + + + + + Indicates to that the client no longer needs this dwl_ipc_output. + + + + + + Indicates the client should hide or show themselves. + If the client is visible then hide, if hidden then show. + + + + + + Indicates if the output is active. Zero is invalid, nonzero is valid. + + + + + + + Indicates that a tag has been updated. + + + + + + + + + + Indicates a new layout is selected. + + + + + + + Indicates the title has changed. + + + + + + + Indicates the appid has changed. + + + + + + + Indicates the layout has changed. Since layout symbols are dynamic. + As opposed to the zdwl_ipc_manager.layout event, this should take precendence when displaying. + You can ignore the zdwl_ipc_output.layout event. + + + + + + + Indicates that a sequence of status updates have finished and the client should redraw. + + + + + + + + + + + + The tags are updated as follows: + new_tags = (current_tags AND and_tags) XOR xor_tags + + + + + + + + + + + + + + Indicates if the selected client on this output is fullscreen. + + + + + + + Indicates if the selected client on this output is floating. + + + + + diff --git a/protocols/meson.build b/protocols/meson.build index 7bd222b..0f6f808 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -15,6 +15,7 @@ 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', + 'dwl-ipc-unstable-v2.xml', ] wayland_sources = [ wayland_scanner_code.process(wayland_xmls), diff --git a/src/bar.cpp b/src/bar.cpp index 2f88fa3..b731220 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -87,8 +87,9 @@ Bar::Bar() if (!_pangoContext) { die("pango_font_map_create_context"); } - for (const auto& tagName : tagNames) { - _tags.push_back({ TagState::None, 0, 0, createComponent(tagName) }); + //for (const auto& tagName : tagNames) { + for (int ti = 1; ti < 10; ti++ ) { + _tags.push_back({ TagState::None, 0, 0, createComponent(std::to_string(ti)) }); } _layoutCmp = createComponent(); _titleCmp = createComponent(); @@ -142,6 +143,16 @@ void Bar::setTag(int tag, int state, int numClients, int focusedClient) t.focusedClient = focusedClient; } +uint32_t Bar::getSelectedTagNumClients() +{ + for (auto &tag : _tags) { + if (tag.state == TagState::Active) { + return tag.numClients; + } + } + return 0; +} + void Bar::setSelected(bool selected) { _selected = selected; @@ -241,22 +252,52 @@ void Bar::render() void Bar::renderTags() { for (auto &tag : _tags) { - setColorScheme( - tag.state & TagState::Active ? colorActive : colorInactive, - tag.state & TagState::Urgent); + setTagColorScheme(tag); renderComponent(tag.component); auto indicators = std::min(tag.numClients, static_cast(_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); + if (showIndicators == 1) { + renderSingleIndicator(tag, indicators); + } else if (showIndicators == 2) { + renderMultiIndicators(tag, indicators); } } } +void Bar::renderMultiIndicators(const Tag& t, int inds) +{ + for (auto ind = 0; ind < inds; ind++) { + auto w = ind == t.focusedClient ? 7 : 3; + cairo_move_to(_painter, t.component.x, ind*2+0.5); + cairo_rel_line_to(_painter, w, 0); + cairo_close_path(_painter); + cairo_set_line_width(_painter, 3); + cairo_stroke(_painter); + } +} + +void Bar::renderSingleIndicator(const Tag& t, int inds) +{ + if (inds > 0) { + auto w = 4; + setColorScheme(colorIndicator); + cairo_move_to(_painter, t.component.x+1, 1); + cairo_rel_line_to(_painter, w, 0); + cairo_rel_line_to(_painter, 0, w); + cairo_rel_line_to(_painter, -w, 0); + cairo_close_path(_painter); + cairo_set_line_width(_painter, 1); + beginFg(); + cairo_stroke_preserve(_painter); + if (t.state == TagState::Active) { + beginBg(); + } else { + setTagColorScheme(t); + beginBg(); + } + cairo_fill(_painter); + } +} + void Bar::renderStatus() { pango_cairo_update_layout(_painter, _statusCmp.pangoLayout.get()); @@ -273,6 +314,13 @@ void Bar::renderStatus() } } +void Bar::setTagColorScheme(const Tag& t) +{ + setColorScheme( + t.state & TagState::Active ? colorActive : colorInactive, + t.state & TagState::Urgent); +} + void Bar::setColorScheme(const ColorScheme& scheme, bool invert) { _colorScheme = invert diff --git a/src/bar.hpp b/src/bar.hpp index 176a1bc..1c45961 100644 --- a/src/bar.hpp +++ b/src/bar.hpp @@ -50,9 +50,12 @@ class Bar { void layerSurfaceConfigure(uint32_t serial, uint32_t width, uint32_t height); void render(); void renderTags(); + void renderMultiIndicators(const Tag& t, int inds); + void renderSingleIndicator(const Tag& t, int inds); void renderStatus(); // low-level rendering + void setTagColorScheme(const Tag& t); void setColorScheme(const ColorScheme& scheme, bool invert = false); void beginFg(); void beginBg(); @@ -65,6 +68,7 @@ public: void show(wl_output* output); void hide(); void setTag(int tag, int state, int numClients, int focusedClient); + uint32_t getSelectedTagNumClients(); void setSelected(bool selected); void setLayout(const std::string& layout); void setTitle(const std::string& title); diff --git a/src/common.hpp b/src/common.hpp index c905358..28e9faf 100644 --- a/src/common.hpp +++ b/src/common.hpp @@ -5,11 +5,14 @@ #include #include #include +#include +#include #include #include #include #include #include "wlr-layer-shell-unstable-v1-client-protocol.h" +#include "dwl-ipc-unstable-v2-client-protocol.h" struct Color { Color() {} @@ -38,6 +41,14 @@ extern wl_display* display; extern wl_compositor* compositor; extern wl_shm* shm; extern zwlr_layer_shell_v1* wlrLayerShell; +static std::vector tagNames; +static std::vector layoutNames; + +void view(Monitor& m, const Arg& arg); +void toggleview(Monitor& m, const Arg& arg); +void setlayout(Monitor& m, const Arg& arg); +void tag(Monitor& m, const Arg& arg); +void toggletag(Monitor& m, const Arg& arg); void spawn(Monitor&, const Arg& arg); void setCloexec(int fd); @@ -65,6 +76,7 @@ WL_DELETER(wl_output, wl_output_release_checked); WL_DELETER(wl_pointer, wl_pointer_release); WL_DELETER(wl_seat, wl_seat_release); WL_DELETER(wl_surface, wl_surface_destroy); +WL_DELETER(zdwl_ipc_output_v2, zdwl_ipc_output_v2_destroy); WL_DELETER(zwlr_layer_surface_v1, zwlr_layer_surface_v1_destroy); WL_DELETER(cairo_t, cairo_destroy); diff --git a/src/main.cpp b/src/main.cpp index 15a749a..937fd8e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,6 +21,7 @@ #include "wlr-layer-shell-unstable-v1-client-protocol.h" #include "xdg-output-unstable-v1-client-protocol.h" #include "xdg-shell-client-protocol.h" +#include "dwl-ipc-unstable-v2-client-protocol.h" #include "common.hpp" #include "config.hpp" #include "bar.hpp" @@ -34,6 +35,7 @@ struct Monitor { bool desiredVisibility {true}; bool hasData; uint32_t tags; + wl_unique_ptr dwlMonitor; }; struct SeatPointer { @@ -67,6 +69,8 @@ wl_display* display; wl_compositor* compositor; wl_shm* shm; zwlr_layer_shell_v1* wlrLayerShell; +zdwl_ipc_manager_v2* dwlWm; +char* log_txt; static xdg_wm_base* xdgWmBase; static zxdg_output_manager_v1* xdgOutputManager; static wl_surface* cursorSurface; @@ -85,6 +89,26 @@ static int statusFifoFd {-1}; static int statusFifoWriter {-1}; static bool quitting {false}; +void view(Monitor& m, const Arg& arg) +{ + zdwl_ipc_output_v2_set_tags(m.dwlMonitor.get(), arg.ui, 1); +} +void toggleview(Monitor& m, const Arg& arg) +{ + zdwl_ipc_output_v2_set_tags(m.dwlMonitor.get(), m.tags ^ arg.ui, 0); +} +void setlayout(Monitor& m, const Arg& arg) +{ + zdwl_ipc_output_v2_set_layout(m.dwlMonitor.get(), arg.ui); +} +void tag(Monitor& m, const Arg& arg) +{ + zdwl_ipc_output_v2_set_client_tags(m.dwlMonitor.get(), 0, arg.ui); +} +void toggletag(Monitor& m, const Arg& arg) +{ + zdwl_ipc_output_v2_set_client_tags(m.dwlMonitor.get(), ~0, arg.ui); +} void spawn(Monitor&, const Arg& arg) { if (fork() == 0) { @@ -189,11 +213,88 @@ static const struct wl_seat_listener seatListener = { .name = [](void*, wl_seat*, const char* name) { } }; +static const struct zdwl_ipc_manager_v2_listener dwlWmListener = { + .tags = [](void*, zdwl_ipc_manager_v2*, uint32_t tag_cnt) { + for (uint32_t ti=0; ti<=tag_cnt; ti++) { + tagNames.push_back(std::to_string(ti)); + } + }, + .layout = [](void*, zdwl_ipc_manager_v2*, const char* name) { + layoutNames.push_back(name); + }, +}; + +static const struct zdwl_ipc_output_v2_listener dwlWmMonitorListener { + .toggle_visibility = [](void* mv, zdwl_ipc_output_v2*) { + }, + .active = [](void* mv, zdwl_ipc_output_v2*, uint32_t active) { + auto mon = static_cast(mv); + if (active) { + selmon = mon; + } else if (selmon == mon) { + selmon = nullptr; + } + mon->bar.setSelected(active); + }, + .tag = [](void* mv, zdwl_ipc_output_v2*, uint32_t tag, uint32_t state, uint32_t numClients, uint32_t focusedClient) { + auto mon = static_cast(mv); + int tagState = TagState::None; + if (state & ZDWL_IPC_OUTPUT_V2_TAG_STATE_ACTIVE) + tagState |= TagState::Active; + if (state & ZDWL_IPC_OUTPUT_V2_TAG_STATE_URGENT) + tagState |= TagState::Urgent; + mon->bar.setTag(tag, tagState, numClients, focusedClient); + uint32_t mask = 1 << tag; + if (tagState & TagState::Active) { + mon->tags |= mask; + } else { + mon->tags &= ~mask; + } + }, + .layout = [](void* mv, zdwl_ipc_output_v2*, uint32_t layout) { + auto mon = static_cast(mv); + if (layout == 2) { + char lout[4] = "[M]"; + uint32_t numc = mon->bar.getSelectedTagNumClients(); + if (numc > 0) { + sprintf(lout, "[%d]", numc); + } + mon->bar.setLayout(lout); + } else { + mon->bar.setLayout(layoutNames[layout]); + } + }, + .title = [](void* mv, zdwl_ipc_output_v2*, const char* title) { + auto mon = static_cast(mv); + mon->bar.setTitle(title); + }, + .appid = [](void* mv, zdwl_ipc_output_v2*, const char* appid) { + //auto mon = static_cast(mv); + }, + .layout_symbol = [](void* mv, zdwl_ipc_output_v2*, const char* layout) { + auto mon = static_cast(mv); + mon->bar.setLayout(layout); + }, + .frame = [](void* mv, zdwl_ipc_output_v2*) { + auto mon = static_cast(mv); + mon->hasData = true; + updatemon(*mon); + }, + .fullscreen = [](void* mv, zdwl_ipc_output_v2*, uint32_t is_fullscreen) { + // auto mon = static_cast(mv); + }, + .floating = [](void* mv, zdwl_ipc_output_v2*, uint32_t is_floating) { + //auto mon = static_cast(mv); + } +}; + void setupMonitor(uint32_t name, wl_output* output) { auto& monitor = monitors.emplace_back(Monitor {name, {}, wl_unique_ptr {output}}); 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); + monitor.dwlMonitor.reset(zdwl_ipc_manager_v2_get_output(dwlWm, monitor.wlOutput.get())); + zdwl_ipc_output_v2_add_listener(monitor.dwlMonitor.get(), &dwlWmMonitorListener, &monitor); } void updatemon(Monitor& mon) @@ -219,6 +320,7 @@ void onReady() requireGlobal(shm, "wl_shm"); requireGlobal(wlrLayerShell, "zwlr_layer_shell_v1"); requireGlobal(xdgOutputManager, "zxdg_output_manager_v1"); + requireGlobal(dwlWm, "zdwl_ipc_manager_v2_interface"); setupStatusFifo(); wl_display_roundtrip(display); // roundtrip so we receive all dwl tags etc. @@ -232,7 +334,8 @@ void onReady() bool createFifo(std::string path) { auto result = mkfifo(path.c_str(), 0666); - if (result == 0) { + // if (result == 0) { + if ((result == 0) || (errno == EEXIST)) { auto fd = open(path.c_str(), O_CLOEXEC | O_NONBLOCK | O_RDONLY); if (fd < 0) { diesys("open status fifo reader"); @@ -276,6 +379,7 @@ void setupStatusFifo() static LineBuffer<512> stdinBuffer; static void onStdin() { + return; auto res = stdinBuffer.readLines( [](void* p, size_t size) { return read(0, p, size); }, [](char* p, size_t size) { handleStdin({p, size}); }); @@ -409,6 +513,10 @@ void onGlobalAdd(void*, wl_registry* registry, uint32_t name, const char* interf xdg_wm_base_add_listener(xdgWmBase, &xdgWmBaseListener, nullptr); return; } + if (reg.handle(dwlWm, zdwl_ipc_manager_v2_interface, 1)) { + zdwl_ipc_manager_v2_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 {wlSeat}}); wl_seat_add_listener(wlSeat, &seatListener, &seat); @@ -436,6 +544,7 @@ static const struct wl_registry_listener registry_listener = { int main(int argc, char* argv[]) { int opt; + while ((opt = getopt(argc, argv, "chvs:")) != -1) { switch (opt) { case 's':