mirror of
https://github.com/DreamMaoMao/maomaowm.git
synced 2025-10-29 05:40:21 -04:00
project: add mmsg as sub project
This commit is contained in:
parent
e7e7a86b8c
commit
5780d6b7df
7 changed files with 1143 additions and 2 deletions
|
|
@ -1,3 +1,3 @@
|
|||
#!/usr/bin/bash
|
||||
|
||||
clang-format -i src/*/*.h -i src/*/*.c -i src/mango.c
|
||||
clang-format -i src/*/*.h -i src/*/*.c -i src/mango.c -i mmsg/src/mmsg.c -i mmsg/src/arg.h -i mmsg/src/dynarr.h
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ project('mango', ['c', 'cpp'],
|
|||
)
|
||||
|
||||
subdir('protocols')
|
||||
subdir('mmsg')
|
||||
|
||||
is_nixos = false
|
||||
os_release = run_command('cat', '/etc/os-release', check: false)
|
||||
|
|
@ -120,4 +121,4 @@ desktop_install_dir = join_paths(prefix, 'share/wayland-sessions')
|
|||
install_data('mango.desktop', install_dir : desktop_install_dir)
|
||||
|
||||
# 安装 config.conf
|
||||
install_data('config.conf', install_dir : join_paths(sysconfdir, 'mango'))
|
||||
install_data('config.conf', install_dir : join_paths(sysconfdir, 'mango'))
|
||||
|
|
|
|||
33
mmsg/meson.build
Normal file
33
mmsg/meson.build
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
wayland_scanner = find_program('wayland-scanner')
|
||||
|
||||
dwl_ipc_protocol = 'protocols/dwl-ipc-unstable-v2.xml'
|
||||
|
||||
dwl_ipc_header = custom_target(
|
||||
'dwl-ipc-header',
|
||||
input: dwl_ipc_protocol,
|
||||
output: 'dwl-ipc-unstable-v2-protocol.h',
|
||||
command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'],
|
||||
build_by_default: true,
|
||||
)
|
||||
|
||||
dwl_ipc_source = custom_target(
|
||||
'dwl-ipc-source',
|
||||
input: dwl_ipc_protocol,
|
||||
output: 'dwl-ipc-unstable-v2-protocol.c',
|
||||
command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'],
|
||||
build_by_default: true,
|
||||
)
|
||||
|
||||
executable('mmsg',
|
||||
'src/mmsg.c',
|
||||
dwl_ipc_source,
|
||||
dependencies: [
|
||||
dependency('wayland-client'),
|
||||
],
|
||||
include_directories: include_directories('.'),
|
||||
install: true,
|
||||
c_args: [
|
||||
'-g',
|
||||
'-Wno-unused-function',
|
||||
],
|
||||
)
|
||||
246
mmsg/protocols/dwl-ipc-unstable-v2.xml
Normal file
246
mmsg/protocols/dwl-ipc-unstable-v2.xml
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This is largely ripped from somebar's ipc patchset; just with some personal modifications.
|
||||
I would probably just submit raphi's patchset but I don't think that would be polite.
|
||||
-->
|
||||
<protocol name="dwl_ipc_unstable_v2">
|
||||
<description summary="inter-proccess-communication about dwl's state">
|
||||
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.
|
||||
</description>
|
||||
|
||||
<interface name="zdwl_ipc_manager_v2" version="2">
|
||||
<description summary="manage dwl state">
|
||||
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.
|
||||
</description>
|
||||
|
||||
<request name="release" type="destructor">
|
||||
<description summary="release dwl_ipc_manager">
|
||||
Indicates that the client will not the dwl_ipc_manager object anymore.
|
||||
Objects created through this instance are not affected.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="get_output">
|
||||
<description summary="get a dwl_ipc_outout for a wl_output">
|
||||
Get a dwl_ipc_outout for the specified wl_output.
|
||||
</description>
|
||||
<arg name="id" type="new_id" interface="zdwl_ipc_output_v2"/>
|
||||
<arg name="output" type="object" interface="wl_output"/>
|
||||
</request>
|
||||
|
||||
<event name="tags">
|
||||
<description summary="Announces tag amount">
|
||||
This event is sent after binding.
|
||||
A roundtrip after binding guarantees the client recieved all tags.
|
||||
</description>
|
||||
<arg name="amount" type="uint"/>
|
||||
</event>
|
||||
|
||||
<event name="layout">
|
||||
<description summary="Announces a layout">
|
||||
This event is sent after binding.
|
||||
A roundtrip after binding guarantees the client recieved all layouts.
|
||||
</description>
|
||||
<arg name="name" type="string"/>
|
||||
</event>
|
||||
</interface>
|
||||
|
||||
<interface name="zdwl_ipc_output_v2" version="2">
|
||||
<description summary="control dwl output">
|
||||
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.
|
||||
</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>
|
||||
|
||||
<request name="release" type="destructor">
|
||||
<description summary="release dwl_ipc_outout">
|
||||
Indicates to that the client no longer needs this dwl_ipc_output.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<event name="toggle_visibility">
|
||||
<description summary="Toggle client visibilty">
|
||||
Indicates the client should hide or show themselves.
|
||||
If the client is visible then hide, if hidden then show.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<event name="active">
|
||||
<description summary="Update the selected output.">
|
||||
Indicates if the output is active. Zero is invalid, nonzero is valid.
|
||||
</description>
|
||||
<arg name="active" type="uint"/>
|
||||
</event>
|
||||
|
||||
<event name="tag">
|
||||
<description summary="Update the state of a tag.">
|
||||
Indicates that a tag has been updated.
|
||||
</description>
|
||||
<arg name="tag" type="uint" summary="Index of the tag"/>
|
||||
<arg name="state" type="uint" enum="tag_state" summary="The state of the tag."/>
|
||||
<arg name="clients" type="uint" summary="The number of clients in the tag."/>
|
||||
<arg name="focused" type="uint" summary="If there is a focused client. Nonzero being valid, zero being invalid."/>
|
||||
</event>
|
||||
|
||||
<event name="layout">
|
||||
<description summary="Update the layout.">
|
||||
Indicates a new layout is selected.
|
||||
</description>
|
||||
<arg name="layout" type="uint" summary="Index of the layout."/>
|
||||
</event>
|
||||
|
||||
<event name="title">
|
||||
<description summary="Update the title.">
|
||||
Indicates the title has changed.
|
||||
</description>
|
||||
<arg name="title" type="string" summary="The new title name."/>
|
||||
</event>
|
||||
|
||||
<event name="appid" since="1">
|
||||
<description summary="Update the appid.">
|
||||
Indicates the appid has changed.
|
||||
</description>
|
||||
<arg name="appid" type="string" summary="The new appid."/>
|
||||
</event>
|
||||
|
||||
<event name="layout_symbol" since="1">
|
||||
<description summary="Update the current layout symbol">
|
||||
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.
|
||||
</description>
|
||||
<arg name="layout" type="string" summary="The new layout"/>
|
||||
</event>
|
||||
|
||||
<event name="frame">
|
||||
<description summary="The update sequence is done.">
|
||||
Indicates that a sequence of status updates have finished and the client should redraw.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<request name="set_tags">
|
||||
<description summary="Set the active tags of this output"/>
|
||||
<arg name="tagmask" type="uint" summary="bitmask of the tags that should be set."/>
|
||||
<arg name="toggle_tagset" type="uint" summary="toggle the selected tagset, zero for invalid, nonzero for valid."/>
|
||||
</request>
|
||||
|
||||
<request name="set_client_tags">
|
||||
<description summary="Set the tags of the focused client.">
|
||||
The tags are updated as follows:
|
||||
new_tags = (current_tags AND and_tags) XOR xor_tags
|
||||
</description>
|
||||
<arg name="and_tags" type="uint"/>
|
||||
<arg name="xor_tags" type="uint"/>
|
||||
</request>
|
||||
|
||||
<request name="set_layout">
|
||||
<description summary="Set the layout of this output"/>
|
||||
<arg name="index" type="uint" summary="index of a layout recieved by dwl_ipc_manager.layout"/>
|
||||
</request>
|
||||
|
||||
<request name="quit" since="2">
|
||||
<description summary="Quit mango">This request allows clients to instruct the compositor to quit mango.</description>
|
||||
</request>
|
||||
|
||||
<request name="dispatch" since="2">
|
||||
<description summary="Set the active tags of this output"/>
|
||||
<arg name="dispatch" type="string" summary="dispatch name."/>
|
||||
<arg name="arg1" type="string" summary="arg1."/>
|
||||
<arg name="arg2" type="string" summary="arg2."/>
|
||||
<arg name="arg3" type="string" summary="arg3."/>
|
||||
<arg name="arg4" type="string" summary="arg4."/>
|
||||
<arg name="arg5" type="string" summary="arg5."/>
|
||||
</request>
|
||||
|
||||
<!-- Version 2 -->
|
||||
<event name="fullscreen" since="2">
|
||||
<description summary="Update fullscreen status">
|
||||
Indicates if the selected client on this output is fullscreen.
|
||||
</description>
|
||||
<arg name="is_fullscreen" type="uint" summary="If the selected client is fullscreen. Nonzero is valid, zero invalid"/>
|
||||
</event>
|
||||
|
||||
<event name="floating" since="2">
|
||||
<description summary="Update the floating status">
|
||||
Indicates if the selected client on this output is floating.
|
||||
</description>
|
||||
<arg name="is_floating" type="uint" summary="If the selected client is floating. Nonzero is valid, zero invalid"/>
|
||||
</event>
|
||||
|
||||
<event name="x" since="2">
|
||||
<description summary="Update the x coordinates">
|
||||
Indicates if x coordinates of the selected client.
|
||||
</description>
|
||||
<arg name="x" type="int" summary="x coordinate of the selected client"/>
|
||||
</event>
|
||||
|
||||
<event name="y" since="2">
|
||||
<description summary="Update the y coordinates">
|
||||
Indicates if y coordinates of the selected client.
|
||||
</description>
|
||||
<arg name="y" type="int" summary="y coordinate of the selected client"/>
|
||||
</event>
|
||||
|
||||
<event name="width" since="2">
|
||||
<description summary="Update the width">
|
||||
Indicates if width of the selected client.
|
||||
</description>
|
||||
<arg name="width" type="int" summary="width of the selected client"/>
|
||||
</event>
|
||||
|
||||
<event name="height" since="2">
|
||||
<description summary="Update the height">
|
||||
Indicates if height of the selected client.
|
||||
</description>
|
||||
<arg name="height" type="int" summary="height of the selected client"/>
|
||||
</event>
|
||||
|
||||
<event name="last_layer" since="2">
|
||||
<description summary="last map layer.">
|
||||
last map layer.
|
||||
</description>
|
||||
<arg name="last_layer" type="string" summary="last map layer."/>
|
||||
</event>
|
||||
|
||||
<event name="kb_layout" since="2">
|
||||
<description summary="current keyboard layout.">
|
||||
current keyboard layout.
|
||||
</description>
|
||||
<arg name="kb_layout" type="string" summary="current keyboard layout."/>
|
||||
</event>
|
||||
|
||||
<event name="keymode" since="2">
|
||||
<description summary="current keybind mode.">
|
||||
current keybind mode.
|
||||
</description>
|
||||
<arg name="keymode" type="string" summary="current keybind mode."/>
|
||||
</event>
|
||||
|
||||
</interface>
|
||||
|
||||
</protocol>
|
||||
65
mmsg/src/arg.h
Normal file
65
mmsg/src/arg.h
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copy me if you can.
|
||||
* by 20h
|
||||
*/
|
||||
|
||||
#ifndef ARG_H__
|
||||
#define ARG_H__
|
||||
|
||||
extern char *argv0;
|
||||
|
||||
/* use main(int argc, char *argv[]) */
|
||||
#define ARGBEGIN \
|
||||
for (argv0 = *argv, argv++, argc--; \
|
||||
argv[0] && argv[0][0] == '-' && argv[0][1]; argc--, argv++) { \
|
||||
char argc_; \
|
||||
char **argv_; \
|
||||
int brk_; \
|
||||
if (argv[0][1] == '-' && argv[0][2] == '\0') { \
|
||||
argv++; \
|
||||
argc--; \
|
||||
break; \
|
||||
} \
|
||||
for (brk_ = 0, argv[0]++, argv_ = argv; argv[0][0] && !brk_; \
|
||||
argv[0]++) { \
|
||||
if (argv_ != argv) \
|
||||
break; \
|
||||
argc_ = argv[0][0]; \
|
||||
switch (argc_)
|
||||
|
||||
/* Handles obsolete -NUM syntax */
|
||||
#define ARGNUM \
|
||||
case '0': \
|
||||
case '1': \
|
||||
case '2': \
|
||||
case '3': \
|
||||
case '4': \
|
||||
case '5': \
|
||||
case '6': \
|
||||
case '7': \
|
||||
case '8': \
|
||||
case '9'
|
||||
|
||||
#define ARGEND \
|
||||
} \
|
||||
}
|
||||
|
||||
#define ARGC() argc_
|
||||
|
||||
#define ARGNUMF() (brk_ = 1, estrtonum(argv[0], 0, INT_MAX))
|
||||
|
||||
#define EARGF(x) \
|
||||
((argv[0][1] == '\0' && argv[1] == NULL) \
|
||||
? ((x), abort(), (char *)0) \
|
||||
: (brk_ = 1, \
|
||||
(argv[0][1] != '\0') ? (&argv[0][1]) : (argc--, argv++, argv[0])))
|
||||
|
||||
#define ARGF() \
|
||||
((argv[0][1] == '\0' && argv[1] == NULL) \
|
||||
? (char *)0 \
|
||||
: (brk_ = 1, \
|
||||
(argv[0][1] != '\0') ? (&argv[0][1]) : (argc--, argv++, argv[0])))
|
||||
|
||||
#define LNGARG() &argv[0][0]
|
||||
|
||||
#endif
|
||||
30
mmsg/src/dynarr.h
Normal file
30
mmsg/src/dynarr.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef DYNARR_H__
|
||||
#define DYNARR_H__
|
||||
|
||||
#define DYNARR_DEF(t) \
|
||||
struct { \
|
||||
t *arr; \
|
||||
size_t len, cap, size; \
|
||||
}
|
||||
|
||||
#define DYNARR_INIT(p) \
|
||||
((p)->arr = reallocarray((p)->arr, ((p)->cap = 1), \
|
||||
((p)->size = sizeof(((p)->arr[0])))))
|
||||
|
||||
#define DYNARR_FINI(p) free((p)->arr)
|
||||
|
||||
#define DYNARR_PUSH(p, v) \
|
||||
do { \
|
||||
if ((p)->len >= (p)->cap) { \
|
||||
while ((p)->len >= ((p)->cap *= 2)) \
|
||||
; \
|
||||
(p)->arr = reallocarray((p)->arr, (p)->cap, (p)->size); \
|
||||
} \
|
||||
(p)->arr[(p)->len++] = (v); \
|
||||
} while (0)
|
||||
|
||||
#define DYNARR_POP(p) ((p)->arr[(p)->len--])
|
||||
|
||||
#define DYNARR_CLR(p) ((p)->len = 0)
|
||||
|
||||
#endif
|
||||
766
mmsg/src/mmsg.c
Normal file
766
mmsg/src/mmsg.c
Normal file
|
|
@ -0,0 +1,766 @@
|
|||
#include "arg.h"
|
||||
#include "dwl-ipc-unstable-v2-protocol.h"
|
||||
#include "dynarr.h"
|
||||
#include <ctype.h>
|
||||
#include <poll.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wayland-client.h>
|
||||
#include <wayland-util.h>
|
||||
|
||||
#define die(fmt, ...) \
|
||||
do { \
|
||||
fprintf(stderr, fmt "\n", ##__VA_ARGS__); \
|
||||
exit(EXIT_FAILURE); \
|
||||
} while (0)
|
||||
|
||||
char *argv0;
|
||||
|
||||
static enum {
|
||||
NONE = 0,
|
||||
SET = 1 << 0,
|
||||
GET = 1 << 1,
|
||||
WATCH = 1 << 2 | GET,
|
||||
} mode = NONE;
|
||||
|
||||
static int Oflag;
|
||||
static int Tflag;
|
||||
static int Lflag;
|
||||
static int oflag;
|
||||
static int tflag;
|
||||
static int lflag;
|
||||
static int cflag;
|
||||
static int vflag;
|
||||
static int mflag;
|
||||
static int fflag;
|
||||
static int qflag;
|
||||
static int dflag;
|
||||
static int xflag;
|
||||
static int eflag;
|
||||
static int kflag;
|
||||
static int bflag;
|
||||
|
||||
static uint32_t occ, seltags, total_clients, urg;
|
||||
|
||||
static char *output_name;
|
||||
static int tagcount;
|
||||
static char *tagset;
|
||||
static char *layout_name;
|
||||
static int layoutcount, layout_idx;
|
||||
static char *client_tags;
|
||||
static char *dispatch_cmd;
|
||||
static char *dispatch_arg1;
|
||||
static char *dispatch_arg2;
|
||||
static char *dispatch_arg3;
|
||||
static char *dispatch_arg4;
|
||||
static char *dispatch_arg5;
|
||||
|
||||
struct output {
|
||||
char *output_name;
|
||||
uint32_t name;
|
||||
};
|
||||
static DYNARR_DEF(struct output) outputs;
|
||||
|
||||
static struct wl_display *display;
|
||||
static struct zdwl_ipc_manager_v2 *dwl_ipc_manager;
|
||||
|
||||
// 为每个回调定义专用的空函数
|
||||
static void noop_geometry(void *data, struct wl_output *wl_output, int32_t x,
|
||||
int32_t y, int32_t physical_width,
|
||||
int32_t physical_height, int32_t subpixel,
|
||||
const char *make, const char *model,
|
||||
int32_t transform) {}
|
||||
|
||||
static void noop_mode(void *data, struct wl_output *wl_output, uint32_t flags,
|
||||
int32_t width, int32_t height, int32_t refresh) {}
|
||||
|
||||
static void noop_done(void *data, struct wl_output *wl_output) {}
|
||||
|
||||
static void noop_scale(void *data, struct wl_output *wl_output,
|
||||
int32_t factor) {}
|
||||
|
||||
static void noop_description(void *data, struct wl_output *wl_output,
|
||||
const char *description) {}
|
||||
|
||||
// 将 n 转换为 9 位二进制字符串,结果存入 buf(至少长度 10)
|
||||
void bin_str_9bits(char *buf, unsigned int n) {
|
||||
for (int i = 8; i >= 0; i--) {
|
||||
*buf++ = ((n >> i) & 1) ? '1' : '0';
|
||||
}
|
||||
*buf = '\0'; // 字符串结尾
|
||||
}
|
||||
|
||||
static void dwl_ipc_tags(void *data,
|
||||
struct zdwl_ipc_manager_v2 *dwl_ipc_manager,
|
||||
uint32_t count) {
|
||||
tagcount = count;
|
||||
if (Tflag && mode & GET)
|
||||
printf("%d\n", tagcount);
|
||||
}
|
||||
|
||||
static void dwl_ipc_layout(void *data,
|
||||
struct zdwl_ipc_manager_v2 *dwl_ipc_manager,
|
||||
const char *name) {
|
||||
if (lflag && mode & SET && strcmp(layout_name, name) == 0)
|
||||
layout_idx = layoutcount;
|
||||
if (Lflag && mode & GET)
|
||||
printf("%s\n", name);
|
||||
layoutcount++;
|
||||
}
|
||||
|
||||
static const struct zdwl_ipc_manager_v2_listener dwl_ipc_listener = {
|
||||
.tags = dwl_ipc_tags, .layout = dwl_ipc_layout};
|
||||
|
||||
static void
|
||||
dwl_ipc_output_toggle_visibility(void *data,
|
||||
struct zdwl_ipc_output_v2 *dwl_ipc_output) {
|
||||
if (!vflag)
|
||||
return;
|
||||
char *output_name = data;
|
||||
if (output_name)
|
||||
printf("%s ", output_name);
|
||||
printf("toggle\n");
|
||||
}
|
||||
|
||||
static void dwl_ipc_output_active(void *data,
|
||||
struct zdwl_ipc_output_v2 *dwl_ipc_output,
|
||||
uint32_t active) {
|
||||
if (!oflag) {
|
||||
if (mode & SET && !output_name && active)
|
||||
output_name = strdup(data);
|
||||
return;
|
||||
}
|
||||
char *output_name = data;
|
||||
if (output_name)
|
||||
printf("%s ", output_name);
|
||||
printf("selmon %u\n", active ? 1 : 0);
|
||||
}
|
||||
|
||||
static void dwl_ipc_output_tag(void *data,
|
||||
struct zdwl_ipc_output_v2 *dwl_ipc_output,
|
||||
uint32_t tag, uint32_t state, uint32_t clients,
|
||||
uint32_t focused) {
|
||||
if (!tflag)
|
||||
return;
|
||||
if (state == ZDWL_IPC_OUTPUT_V2_TAG_STATE_ACTIVE)
|
||||
seltags |= 1 << tag;
|
||||
if (state == ZDWL_IPC_OUTPUT_V2_TAG_STATE_URGENT)
|
||||
urg |= 1 << tag;
|
||||
if (clients > 0)
|
||||
occ |= 1 << tag;
|
||||
|
||||
// 累计所有 tag 的 clients 总数
|
||||
total_clients += clients;
|
||||
|
||||
if (!(mode & GET))
|
||||
return;
|
||||
char *output_name = data;
|
||||
if (output_name)
|
||||
printf("%s ", output_name);
|
||||
printf("tag %u %u %u %u\n", tag + 1, state, clients, focused);
|
||||
}
|
||||
|
||||
static void dwl_ipc_output_layout(void *data,
|
||||
struct zdwl_ipc_output_v2 *dwl_ipc_output,
|
||||
uint32_t layout) {}
|
||||
|
||||
static void dwl_ipc_output_layout_symbol(
|
||||
void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, const char *layout) {
|
||||
if (!(lflag && mode & GET))
|
||||
return;
|
||||
char *output_name = data;
|
||||
if (output_name)
|
||||
printf("%s ", output_name);
|
||||
printf("layout %s\n", layout);
|
||||
}
|
||||
|
||||
static void dwl_ipc_output_title(void *data,
|
||||
struct zdwl_ipc_output_v2 *dwl_ipc_output,
|
||||
const char *title) {
|
||||
if (!(cflag && mode & GET))
|
||||
return;
|
||||
char *output_name = data;
|
||||
if (output_name)
|
||||
printf("%s ", output_name);
|
||||
printf("title %s\n", title);
|
||||
}
|
||||
|
||||
static void dwl_ipc_output_appid(void *data,
|
||||
struct zdwl_ipc_output_v2 *dwl_ipc_output,
|
||||
const char *appid) {
|
||||
if (!(cflag && mode & GET))
|
||||
return;
|
||||
char *output_name = data;
|
||||
if (output_name)
|
||||
printf("%s ", output_name);
|
||||
printf("appid %s\n", appid);
|
||||
}
|
||||
|
||||
static void dwl_ipc_output_x(void *data,
|
||||
struct zdwl_ipc_output_v2 *dwl_ipc_output,
|
||||
int32_t x) {
|
||||
if (!xflag)
|
||||
return;
|
||||
char *output_name = data;
|
||||
if (output_name)
|
||||
printf("%s ", output_name);
|
||||
printf("x %d\n", x);
|
||||
}
|
||||
|
||||
static void dwl_ipc_output_y(void *data,
|
||||
struct zdwl_ipc_output_v2 *dwl_ipc_output,
|
||||
int32_t y) {
|
||||
if (!xflag)
|
||||
return;
|
||||
char *output_name = data;
|
||||
if (output_name)
|
||||
printf("%s ", output_name);
|
||||
printf("y %d\n", y);
|
||||
}
|
||||
|
||||
static void dwl_ipc_output_width(void *data,
|
||||
struct zdwl_ipc_output_v2 *dwl_ipc_output,
|
||||
int32_t width) {
|
||||
if (!xflag)
|
||||
return;
|
||||
char *output_name = data;
|
||||
if (output_name)
|
||||
printf("%s ", output_name);
|
||||
printf("width %d\n", width);
|
||||
}
|
||||
|
||||
static void dwl_ipc_output_height(void *data,
|
||||
struct zdwl_ipc_output_v2 *dwl_ipc_output,
|
||||
int32_t height) {
|
||||
if (!xflag)
|
||||
return;
|
||||
char *output_name = data;
|
||||
if (output_name)
|
||||
printf("%s ", output_name);
|
||||
printf("height %d\n", height);
|
||||
}
|
||||
|
||||
static void dwl_ipc_output_last_layer(void *data,
|
||||
struct zdwl_ipc_output_v2 *dwl_ipc_output,
|
||||
const char *last_layer) {
|
||||
if (!eflag)
|
||||
return;
|
||||
char *output_name = data;
|
||||
if (output_name)
|
||||
printf("%s ", output_name);
|
||||
printf("last_layer %s\n", last_layer);
|
||||
}
|
||||
|
||||
static void dwl_ipc_output_kb_layout(void *data,
|
||||
struct zdwl_ipc_output_v2 *dwl_ipc_output,
|
||||
const char *kb_layout) {
|
||||
if (!kflag)
|
||||
return;
|
||||
char *output_name = data;
|
||||
if (output_name)
|
||||
printf("%s ", output_name);
|
||||
printf("kb_layout %s\n", kb_layout);
|
||||
}
|
||||
|
||||
static void dwl_ipc_output_keymode(void *data,
|
||||
struct zdwl_ipc_output_v2 *dwl_ipc_output,
|
||||
const char *keymode) {
|
||||
if (!bflag)
|
||||
return;
|
||||
char *output_name = data;
|
||||
if (output_name)
|
||||
printf("%s ", output_name);
|
||||
printf("keymode %s\n", keymode);
|
||||
}
|
||||
|
||||
static void dwl_ipc_output_fullscreen(void *data,
|
||||
struct zdwl_ipc_output_v2 *dwl_ipc_output,
|
||||
uint32_t is_fullscreen) {
|
||||
if (!mflag)
|
||||
return;
|
||||
char *output_name = data;
|
||||
if (output_name)
|
||||
printf("%s ", output_name);
|
||||
printf("fullscreen %u\n", is_fullscreen);
|
||||
}
|
||||
|
||||
static void dwl_ipc_output_floating(void *data,
|
||||
struct zdwl_ipc_output_v2 *dwl_ipc_output,
|
||||
uint32_t is_floating) {
|
||||
if (!fflag)
|
||||
return;
|
||||
char *output_name = data;
|
||||
if (output_name)
|
||||
printf("%s ", output_name);
|
||||
printf("floating %u\n", is_floating);
|
||||
}
|
||||
|
||||
static void dwl_ipc_output_frame(void *data,
|
||||
struct zdwl_ipc_output_v2 *dwl_ipc_output) {
|
||||
if (mode & SET) {
|
||||
if (data && (!output_name || strcmp(output_name, (char *)data)))
|
||||
return;
|
||||
if (qflag) {
|
||||
zdwl_ipc_output_v2_quit(dwl_ipc_output);
|
||||
}
|
||||
if (lflag) {
|
||||
zdwl_ipc_output_v2_set_layout(dwl_ipc_output, layout_idx);
|
||||
}
|
||||
if (tflag) {
|
||||
uint32_t mask = seltags;
|
||||
char *t = tagset;
|
||||
int i = 0;
|
||||
|
||||
for (; *t && *t >= '0' && *t <= '9'; t++)
|
||||
i = *t - '0' + i * 10;
|
||||
|
||||
if (!*t)
|
||||
mask = 1 << (i - 1);
|
||||
|
||||
for (; *t; t++, i++) {
|
||||
switch (*t) {
|
||||
case '-':
|
||||
mask &= ~(1 << (i - 1));
|
||||
break;
|
||||
case '+':
|
||||
mask |= 1 << (i - 1);
|
||||
break;
|
||||
case '^':
|
||||
mask ^= 1 << (i - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((i - 1) > tagcount)
|
||||
die("bad tagset %s", tagset);
|
||||
|
||||
zdwl_ipc_output_v2_set_tags(dwl_ipc_output, mask, 0);
|
||||
}
|
||||
if (cflag) {
|
||||
uint32_t and = ~0, xor = 0;
|
||||
char *t = client_tags;
|
||||
int i = 0;
|
||||
|
||||
for (; *t && *t >= '0' && *t <= '9'; t++)
|
||||
i = *t - '0' + i * 10;
|
||||
|
||||
if (!*t)
|
||||
t = "+";
|
||||
|
||||
for (; *t; t++, i++) {
|
||||
switch (*t) {
|
||||
case '-':
|
||||
and &= ~(1 << (i - 1));
|
||||
break;
|
||||
case '+':
|
||||
and &= ~(1 << (i - 1));
|
||||
xor |= 1 << (i - 1);
|
||||
break;
|
||||
case '^':
|
||||
xor |= 1 << (i - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((i - 1) > tagcount)
|
||||
die("bad client tagset %s", client_tags);
|
||||
|
||||
zdwl_ipc_output_v2_set_client_tags(dwl_ipc_output, and, xor);
|
||||
}
|
||||
if (dflag) {
|
||||
zdwl_ipc_output_v2_dispatch(
|
||||
dwl_ipc_output, dispatch_cmd, dispatch_arg1, dispatch_arg2,
|
||||
dispatch_arg3, dispatch_arg4, dispatch_arg5);
|
||||
}
|
||||
wl_display_flush(display);
|
||||
exit(0);
|
||||
} else {
|
||||
if (tflag) {
|
||||
char *output_name = data;
|
||||
|
||||
printf("%s clients %u\n", output_name, total_clients);
|
||||
|
||||
char occ_str[10], seltags_str[10], urg_str[10];
|
||||
|
||||
bin_str_9bits(occ_str, occ);
|
||||
bin_str_9bits(seltags_str, seltags);
|
||||
bin_str_9bits(urg_str, urg);
|
||||
printf("%s tags %u %u %u\n", output_name, occ, seltags, urg);
|
||||
printf("%s tags %s %s %s\n", output_name, occ_str, seltags_str,
|
||||
urg_str);
|
||||
occ = seltags = total_clients = urg = 0;
|
||||
}
|
||||
}
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
static const struct zdwl_ipc_output_v2_listener dwl_ipc_output_listener = {
|
||||
.toggle_visibility = dwl_ipc_output_toggle_visibility,
|
||||
.active = dwl_ipc_output_active,
|
||||
.tag = dwl_ipc_output_tag,
|
||||
.layout = dwl_ipc_output_layout,
|
||||
.title = dwl_ipc_output_title,
|
||||
.appid = dwl_ipc_output_appid,
|
||||
.layout_symbol = dwl_ipc_output_layout_symbol,
|
||||
.fullscreen = dwl_ipc_output_fullscreen,
|
||||
.floating = dwl_ipc_output_floating,
|
||||
.x = dwl_ipc_output_x,
|
||||
.y = dwl_ipc_output_y,
|
||||
.width = dwl_ipc_output_width,
|
||||
.height = dwl_ipc_output_height,
|
||||
.last_layer = dwl_ipc_output_last_layer,
|
||||
.kb_layout = dwl_ipc_output_kb_layout,
|
||||
.keymode = dwl_ipc_output_keymode,
|
||||
.frame = dwl_ipc_output_frame,
|
||||
};
|
||||
|
||||
static void wl_output_name(void *data, struct wl_output *output,
|
||||
const char *name) {
|
||||
if (outputs.arr) {
|
||||
struct output *o = (struct output *)data;
|
||||
o->output_name = strdup(name);
|
||||
printf("+ ");
|
||||
}
|
||||
if (Oflag)
|
||||
printf("%s\n", name);
|
||||
if (output_name && strcmp(output_name, name) != 0) {
|
||||
wl_output_release(output);
|
||||
return;
|
||||
}
|
||||
struct zdwl_ipc_output_v2 *dwl_ipc_output =
|
||||
zdwl_ipc_manager_v2_get_output(dwl_ipc_manager, output);
|
||||
zdwl_ipc_output_v2_add_listener(dwl_ipc_output, &dwl_ipc_output_listener,
|
||||
output_name ? NULL : strdup(name));
|
||||
}
|
||||
|
||||
static const struct wl_output_listener output_listener = {
|
||||
.geometry = noop_geometry,
|
||||
.mode = noop_mode,
|
||||
.done = noop_done,
|
||||
.scale = noop_scale,
|
||||
.name = wl_output_name,
|
||||
.description = noop_description,
|
||||
};
|
||||
|
||||
static void global_add(void *data, struct wl_registry *wl_registry,
|
||||
uint32_t name, const char *interface, uint32_t version) {
|
||||
if (strcmp(interface, wl_output_interface.name) == 0) {
|
||||
struct wl_output *o =
|
||||
wl_registry_bind(wl_registry, name, &wl_output_interface,
|
||||
WL_OUTPUT_NAME_SINCE_VERSION);
|
||||
if (!outputs.arr) {
|
||||
wl_output_add_listener(o, &output_listener, NULL);
|
||||
} else {
|
||||
DYNARR_PUSH(&outputs, (struct output){.name = name});
|
||||
wl_output_add_listener(o, &output_listener,
|
||||
&outputs.arr[outputs.len - 1]);
|
||||
}
|
||||
} else if (strcmp(interface, zdwl_ipc_manager_v2_interface.name) == 0) {
|
||||
dwl_ipc_manager = wl_registry_bind(wl_registry, name,
|
||||
&zdwl_ipc_manager_v2_interface, 2);
|
||||
zdwl_ipc_manager_v2_add_listener(dwl_ipc_manager, &dwl_ipc_listener,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void global_remove(void *data, struct wl_registry *wl_registry,
|
||||
uint32_t name) {
|
||||
if (!outputs.arr)
|
||||
return;
|
||||
struct output *o = outputs.arr;
|
||||
for (size_t i = 0; i < outputs.len; i++, o++) {
|
||||
if (o->name == name) {
|
||||
printf("- %s\n", o->output_name);
|
||||
free(o->output_name);
|
||||
*o = DYNARR_POP(&outputs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const struct wl_registry_listener registry_listener = {
|
||||
.global = global_add,
|
||||
.global_remove = global_remove,
|
||||
};
|
||||
|
||||
static void usage(void) {
|
||||
fprintf(stderr,
|
||||
"usage:"
|
||||
"\t%s [-OTLq]\n"
|
||||
"\t%s [-o <output>] -s [-t <tags>] [-l <layout>] [-c <tags>] [-d "
|
||||
"<cmd>,<arg1>,<arg2>,<arg3>,<arg4>,<arg5>]\n"
|
||||
"\t%s [-o <output>] (-g | -w) [-Ootlcvmfxekb]\n",
|
||||
argv0, argv0, argv0);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
ARGBEGIN {
|
||||
case 'q':
|
||||
qflag = 1;
|
||||
if (!(mode & GET)) {
|
||||
mode = SET;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if (mode != NONE)
|
||||
usage();
|
||||
mode = SET;
|
||||
break;
|
||||
case 'g':
|
||||
if (mode != NONE)
|
||||
usage();
|
||||
mode = GET;
|
||||
break;
|
||||
case 'w':
|
||||
if (mode != NONE)
|
||||
usage();
|
||||
mode = WATCH;
|
||||
break;
|
||||
case 'o':
|
||||
if (mode == SET)
|
||||
output_name = EARGF(usage());
|
||||
else
|
||||
output_name = ARGF();
|
||||
if (!output_name)
|
||||
oflag = 1;
|
||||
break;
|
||||
case 't':
|
||||
tflag = 1;
|
||||
if (!(mode & GET)) {
|
||||
mode = SET;
|
||||
tagset = EARGF(usage());
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
lflag = 1;
|
||||
if (!(mode & GET)) {
|
||||
mode = SET;
|
||||
layout_name = EARGF(usage());
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
cflag = 1;
|
||||
if (!(mode & GET)) {
|
||||
mode = SET;
|
||||
client_tags = EARGF(usage());
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
dflag = 1;
|
||||
if (!(mode & GET)) {
|
||||
mode = SET;
|
||||
char *arg = EARGF(usage());
|
||||
|
||||
// Trim leading and trailing whitespace from entire argument first
|
||||
while (isspace(*arg))
|
||||
arg++;
|
||||
char *end = arg + strlen(arg) - 1;
|
||||
while (end > arg && isspace(*end))
|
||||
end--;
|
||||
*(end + 1) = '\0';
|
||||
|
||||
dispatch_cmd = arg;
|
||||
char *comma1 = strchr(arg, ',');
|
||||
if (comma1) {
|
||||
*comma1 = '\0';
|
||||
|
||||
// Trim trailing whitespace from command
|
||||
end = dispatch_cmd + strlen(dispatch_cmd) - 1;
|
||||
while (end > dispatch_cmd && isspace(*end))
|
||||
end--;
|
||||
*(end + 1) = '\0';
|
||||
|
||||
dispatch_arg1 = comma1 + 1;
|
||||
// Trim leading whitespace from arg1
|
||||
while (isspace(*dispatch_arg1))
|
||||
dispatch_arg1++;
|
||||
|
||||
// Trim trailing whitespace from arg1 before looking for next
|
||||
// comma
|
||||
end = dispatch_arg1 + strlen(dispatch_arg1) - 1;
|
||||
while (end > dispatch_arg1 && isspace(*end))
|
||||
end--;
|
||||
*(end + 1) = '\0';
|
||||
|
||||
char *comma2 = strchr(dispatch_arg1, ',');
|
||||
if (comma2) {
|
||||
*comma2 = '\0';
|
||||
dispatch_arg2 = comma2 + 1;
|
||||
// Trim leading whitespace from arg2
|
||||
while (isspace(*dispatch_arg2))
|
||||
dispatch_arg2++;
|
||||
|
||||
// Trim trailing whitespace from arg2 before looking for
|
||||
// next comma
|
||||
end = dispatch_arg2 + strlen(dispatch_arg2) - 1;
|
||||
while (end > dispatch_arg2 && isspace(*end))
|
||||
end--;
|
||||
*(end + 1) = '\0';
|
||||
|
||||
char *comma3 = strchr(dispatch_arg2, ',');
|
||||
if (comma3) {
|
||||
*comma3 = '\0';
|
||||
dispatch_arg3 = comma3 + 1;
|
||||
// Trim leading whitespace from arg3
|
||||
while (isspace(*dispatch_arg3))
|
||||
dispatch_arg3++;
|
||||
|
||||
// Trim trailing whitespace from arg3 before looking for
|
||||
// next comma
|
||||
end = dispatch_arg3 + strlen(dispatch_arg3) - 1;
|
||||
while (end > dispatch_arg3 && isspace(*end))
|
||||
end--;
|
||||
*(end + 1) = '\0';
|
||||
|
||||
char *comma4 = strchr(dispatch_arg3, ',');
|
||||
if (comma4) {
|
||||
*comma4 = '\0';
|
||||
dispatch_arg4 = comma4 + 1;
|
||||
// Trim leading whitespace from arg4
|
||||
while (isspace(*dispatch_arg4))
|
||||
dispatch_arg4++;
|
||||
|
||||
// Trim trailing whitespace from arg4 before looking
|
||||
// for next comma
|
||||
end = dispatch_arg4 + strlen(dispatch_arg4) - 1;
|
||||
while (end > dispatch_arg4 && isspace(*end))
|
||||
end--;
|
||||
*(end + 1) = '\0';
|
||||
|
||||
char *comma5 = strchr(dispatch_arg4, ',');
|
||||
if (comma5) {
|
||||
*comma5 = '\0';
|
||||
dispatch_arg5 = comma5 + 1;
|
||||
// Trim leading whitespace from arg5
|
||||
while (isspace(*dispatch_arg5))
|
||||
dispatch_arg5++;
|
||||
|
||||
// Trim trailing whitespace from arg5
|
||||
end = dispatch_arg5 + strlen(dispatch_arg5) - 1;
|
||||
while (end > dispatch_arg5 && isspace(*end))
|
||||
end--;
|
||||
*(end + 1) = '\0';
|
||||
} else {
|
||||
dispatch_arg5 = "";
|
||||
}
|
||||
} else {
|
||||
dispatch_arg4 = "";
|
||||
dispatch_arg5 = "";
|
||||
}
|
||||
} else {
|
||||
dispatch_arg3 = "";
|
||||
dispatch_arg4 = "";
|
||||
dispatch_arg5 = "";
|
||||
}
|
||||
} else {
|
||||
dispatch_arg2 = "";
|
||||
dispatch_arg3 = "";
|
||||
dispatch_arg4 = "";
|
||||
dispatch_arg5 = "";
|
||||
}
|
||||
} else {
|
||||
dispatch_arg1 = "";
|
||||
dispatch_arg2 = "";
|
||||
dispatch_arg3 = "";
|
||||
dispatch_arg4 = "";
|
||||
dispatch_arg5 = "";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'O':
|
||||
Oflag = 1;
|
||||
if (mode && !(mode & GET))
|
||||
usage();
|
||||
if (mode & WATCH)
|
||||
DYNARR_INIT(&outputs);
|
||||
else
|
||||
mode = GET;
|
||||
break;
|
||||
case 'T':
|
||||
Tflag = 1;
|
||||
if (mode && mode != GET)
|
||||
usage();
|
||||
mode = GET;
|
||||
break;
|
||||
case 'L':
|
||||
Lflag = 1;
|
||||
if (mode && mode != GET)
|
||||
usage();
|
||||
mode = GET;
|
||||
break;
|
||||
case 'v':
|
||||
vflag = 1;
|
||||
if (mode == SET)
|
||||
usage();
|
||||
mode |= GET;
|
||||
break;
|
||||
case 'm':
|
||||
mflag = 1;
|
||||
if (mode == SET)
|
||||
usage();
|
||||
mode |= GET;
|
||||
break;
|
||||
case 'f':
|
||||
fflag = 1;
|
||||
if (mode == SET)
|
||||
usage();
|
||||
mode |= GET;
|
||||
break;
|
||||
case 'x':
|
||||
xflag = 1;
|
||||
if (mode == SET)
|
||||
usage();
|
||||
mode |= GET;
|
||||
break;
|
||||
case 'e':
|
||||
eflag = 1;
|
||||
if (mode == SET)
|
||||
usage();
|
||||
mode |= GET;
|
||||
break;
|
||||
case 'k':
|
||||
kflag = 1;
|
||||
if (mode == SET)
|
||||
usage();
|
||||
mode |= GET;
|
||||
break;
|
||||
case 'b':
|
||||
bflag = 1;
|
||||
if (mode == SET)
|
||||
usage();
|
||||
mode |= GET;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "bad option %c\n", ARGC());
|
||||
usage();
|
||||
}
|
||||
ARGEND
|
||||
if (mode == NONE)
|
||||
usage();
|
||||
if (mode & GET && !output_name &&
|
||||
!(oflag || tflag || lflag || Oflag || Tflag || Lflag || cflag ||
|
||||
vflag || mflag || fflag || xflag || eflag || kflag || bflag || dflag))
|
||||
oflag = tflag = lflag = cflag = vflag = mflag = fflag = xflag = eflag =
|
||||
kflag = bflag = 1;
|
||||
|
||||
display = wl_display_connect(NULL);
|
||||
if (!display)
|
||||
die("bad display");
|
||||
|
||||
struct wl_registry *registry = wl_display_get_registry(display);
|
||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||
|
||||
wl_display_dispatch(display);
|
||||
wl_display_roundtrip(display);
|
||||
|
||||
if (!dwl_ipc_manager)
|
||||
die("bad dwl-ipc protocol");
|
||||
|
||||
wl_display_roundtrip(display);
|
||||
|
||||
if (mode == WATCH)
|
||||
while (wl_display_dispatch(display) != -1)
|
||||
;
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue