mirror of
https://github.com/DreamMaoMao/maomaowm.git
synced 2026-05-28 21:37:28 -04:00
feat: new ipc impl
This commit is contained in:
parent
c9ac0582e2
commit
324d29e68c
13 changed files with 1205 additions and 1020 deletions
|
|
@ -1,3 +1,3 @@
|
||||||
#!/usr/bin/bash
|
#!/usr/bin/bash
|
||||||
|
|
||||||
clang-format -i src/*/*.h -i src/*/*.c -i src/mango.c -i mmsg/mmsg.c -i mmsg/arg.h -i mmsg/dynarr.h
|
clang-format -i src/*/*.h -i src/*/*.c -i src/mango.c -i mmsg/mmsg.c
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ libinput_dep = dependency('libinput',version: '>=1.27.1')
|
||||||
libwayland_client_dep = dependency('wayland-client')
|
libwayland_client_dep = dependency('wayland-client')
|
||||||
pcre2_dep = dependency('libpcre2-8')
|
pcre2_dep = dependency('libpcre2-8')
|
||||||
pixman_dep = dependency('pixman-1')
|
pixman_dep = dependency('pixman-1')
|
||||||
|
cjson_dep = dependency('libcjson')
|
||||||
|
|
||||||
|
|
||||||
# 获取版本信息
|
# 获取版本信息
|
||||||
|
|
@ -109,6 +110,7 @@ executable('mango',
|
||||||
libwayland_client_dep,
|
libwayland_client_dep,
|
||||||
pcre2_dep,
|
pcre2_dep,
|
||||||
pixman_dep,
|
pixman_dep,
|
||||||
|
cjson_dep,
|
||||||
],
|
],
|
||||||
install : true,
|
install : true,
|
||||||
c_args : c_args,
|
c_args : c_args,
|
||||||
|
|
|
||||||
65
mmsg/arg.h
65
mmsg/arg.h
|
|
@ -1,65 +0,0 @@
|
||||||
/*
|
|
||||||
* Copy me if you can.
|
|
||||||
* by 20h
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ARG_H__
|
|
||||||
#define ARG_H__
|
|
||||||
|
|
||||||
extern char *argv0;
|
|
||||||
|
|
||||||
/* use main(int32_t argc, char *argv[]) */
|
|
||||||
#define ARGBEGIN \
|
|
||||||
for (argv0 = *argv, argv++, argc--; \
|
|
||||||
argv[0] && argv[0][0] == '-' && argv[0][1]; argc--, argv++) { \
|
|
||||||
char argc_; \
|
|
||||||
char **argv_; \
|
|
||||||
int32_t 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
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
#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
|
|
||||||
804
mmsg/mmsg.c
804
mmsg/mmsg.c
|
|
@ -1,754 +1,96 @@
|
||||||
#include "arg.h"
|
#define _GNU_SOURCE
|
||||||
#include "dwl-ipc-unstable-v2-protocol.h"
|
#include <stdbool.h>
|
||||||
#include "dynarr.h"
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <poll.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-client.h>
|
|
||||||
#include <wayland-util.h>
|
|
||||||
|
|
||||||
#define die(fmt, ...) \
|
int main(int argc, char *argv[]) {
|
||||||
do { \
|
if (argc < 2) {
|
||||||
fprintf(stderr, fmt "\n", ##__VA_ARGS__); \
|
fprintf(stderr, "Usage: mmsg <command> [args...]\n");
|
||||||
exit(EXIT_FAILURE); \
|
fprintf(stderr, " get <type> ... one-shot request\n");
|
||||||
} while (0)
|
fprintf(stderr, " watch <type> ... persistent stream\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
char *argv0;
|
|
||||||
|
|
||||||
static enum {
|
|
||||||
NONE = 0,
|
|
||||||
SET = 1 << 0,
|
|
||||||
GET = 1 << 1,
|
|
||||||
WATCH = 1 << 2 | GET,
|
|
||||||
} mode = NONE;
|
|
||||||
|
|
||||||
static int32_t Oflag;
|
|
||||||
static int32_t Tflag;
|
|
||||||
static int32_t Lflag;
|
|
||||||
static int32_t oflag;
|
|
||||||
static int32_t tflag;
|
|
||||||
static int32_t lflag;
|
|
||||||
static int32_t cflag;
|
|
||||||
static int32_t vflag;
|
|
||||||
static int32_t mflag;
|
|
||||||
static int32_t fflag;
|
|
||||||
static int32_t qflag;
|
|
||||||
static int32_t dflag;
|
|
||||||
static int32_t xflag;
|
|
||||||
static int32_t eflag;
|
|
||||||
static int32_t kflag;
|
|
||||||
static int32_t bflag;
|
|
||||||
static int32_t Aflag;
|
|
||||||
|
|
||||||
static uint32_t occ, seltags, total_clients, urg;
|
|
||||||
|
|
||||||
static char *output_name;
|
|
||||||
static int32_t tagcount;
|
|
||||||
static char *tagset;
|
|
||||||
static char *layout_name;
|
|
||||||
static int32_t 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, uint32_t n) {
|
|
||||||
for (int32_t i = 8; i >= 0; i--) {
|
|
||||||
*buf++ = ((n >> i) & 1) ? '1' : '0';
|
|
||||||
}
|
}
|
||||||
*buf = '\0'; // 字符串结尾
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dwl_ipc_tags(void *data,
|
const char *socket_path = getenv("MANGO_INSTANCE_SIGNATURE");
|
||||||
struct zdwl_ipc_manager_v2 *dwl_ipc_manager,
|
if (!socket_path) {
|
||||||
uint32_t count) {
|
fprintf(stderr, "Error: MANGO_INSTANCE_SIGNATURE is not set. Did you "
|
||||||
tagcount = count;
|
"run 'mmsg' in mango?\n");
|
||||||
if (Tflag && mode & GET)
|
return EXIT_FAILURE;
|
||||||
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,
|
int sock = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
struct zdwl_ipc_output_v2 *dwl_ipc_output,
|
if (sock < 0) {
|
||||||
uint32_t tag, uint32_t state, uint32_t clients,
|
perror("socket");
|
||||||
uint32_t focused) {
|
return EXIT_FAILURE;
|
||||||
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_scalefactor(void *data,
|
|
||||||
struct zdwl_ipc_output_v2 *dwl_ipc_output,
|
|
||||||
const uint32_t scalefactor) {
|
|
||||||
if (!Aflag)
|
|
||||||
return;
|
|
||||||
char *output_name = data;
|
|
||||||
if (output_name)
|
|
||||||
printf("%s ", output_name);
|
|
||||||
printf("scale_factor %f\n", scalefactor / 100.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
int32_t 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;
|
|
||||||
int32_t 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);
|
|
||||||
usleep(1000);
|
|
||||||
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 = {
|
struct sockaddr_un addr = {.sun_family = AF_UNIX};
|
||||||
.toggle_visibility = dwl_ipc_output_toggle_visibility,
|
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
|
||||||
.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,
|
|
||||||
.scalefactor = dwl_ipc_output_scalefactor,
|
|
||||||
.frame = dwl_ipc_output_frame,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void wl_output_name(void *data, struct wl_output *output,
|
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||||
const char *name) {
|
perror("connect");
|
||||||
if (outputs.arr) {
|
close(sock);
|
||||||
struct output *o = (struct output *)data;
|
return EXIT_FAILURE;
|
||||||
o->output_name = strdup(name);
|
|
||||||
printf("+ ");
|
|
||||||
}
|
}
|
||||||
if (Oflag)
|
|
||||||
printf("%s\n", name);
|
// 拼接命令,缓冲区大小 4096 以容纳较长参数
|
||||||
if (output_name && strcmp(output_name, name) != 0) {
|
char cmd[4096] = {0};
|
||||||
wl_output_release(output);
|
int offset = 0;
|
||||||
return;
|
for (int i = 1; i < argc; i++) {
|
||||||
|
int n = snprintf(cmd + offset, sizeof(cmd) - offset, "%s%s", argv[i],
|
||||||
|
(i == argc - 1) ? "" : " ");
|
||||||
|
if (n < 0 || n >= (int)(sizeof(cmd) - offset)) {
|
||||||
|
fprintf(stderr, "Error: command too long.\n");
|
||||||
|
close(sock);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
offset += n;
|
||||||
}
|
}
|
||||||
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,
|
int n = snprintf(cmd + offset, sizeof(cmd) - offset, "\n");
|
||||||
.mode = noop_mode,
|
if (n < 0 || n >= (int)(sizeof(cmd) - offset)) {
|
||||||
.done = noop_done,
|
fprintf(stderr, "Error: command too long to append newline.\n");
|
||||||
.scale = noop_scale,
|
close(sock);
|
||||||
.name = wl_output_name,
|
return EXIT_FAILURE;
|
||||||
.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,
|
// 发送命令,使用 MSG_NOSIGNAL 避免 SIGPIPE
|
||||||
uint32_t name) {
|
if (send(sock, cmd, strlen(cmd), MSG_NOSIGNAL) < 0) {
|
||||||
if (!outputs.arr)
|
perror("send");
|
||||||
return;
|
close(sock);
|
||||||
struct output *o = outputs.arr;
|
return EXIT_FAILURE;
|
||||||
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 = {
|
// 将 socket 封装为行缓冲文件流,自动处理 TCP 拆包,按完整行读取
|
||||||
.global = global_add,
|
FILE *stream = fdopen(sock, "r");
|
||||||
.global_remove = global_remove,
|
if (!stream) {
|
||||||
};
|
perror("fdopen");
|
||||||
|
close(sock);
|
||||||
static void usage(void) {
|
return EXIT_FAILURE;
|
||||||
fprintf(stderr,
|
|
||||||
"mmsg - MangoWM IPC\n"
|
|
||||||
"\n"
|
|
||||||
"SYNOPSIS:\n"
|
|
||||||
"\tmmsg [-OTLq]\n"
|
|
||||||
"\tmmsg [-o <output>] -s [-t <tags>] [-l <layout>] [-c <tags>] [-d "
|
|
||||||
"<cmd>,<arg1>,<arg2>,<arg3>,<arg4>,<arg5>]\n"
|
|
||||||
"\tmmsg [-o <output>] (-g | -w) [-OotlcvmfxekbA]\n"
|
|
||||||
"\n"
|
|
||||||
"OPERATION MODES:\n"
|
|
||||||
"\t-g Get values (tags, layout, focused client)\n"
|
|
||||||
"\t-s Set values (switch tags, layouts)\n"
|
|
||||||
"\t-w Watch mode (stream events)\n"
|
|
||||||
"\n"
|
|
||||||
"GENERAL OPTIONS:\n"
|
|
||||||
"\t-O Get all output (monitor) information\n"
|
|
||||||
"\t-T Get number of tags\n"
|
|
||||||
"\t-L Get all available layouts\n"
|
|
||||||
"\t-q Quit mango\n"
|
|
||||||
"\t-o <output> Select output (monitor)\n"
|
|
||||||
"\n"
|
|
||||||
"GET OPTIONS (used with -g or -w):\n"
|
|
||||||
"\t-O Get output name\n"
|
|
||||||
"\t-o Get output (monitor) focus information\n"
|
|
||||||
"\t-t Get selected tags\n"
|
|
||||||
"\t-l Get current layout\n"
|
|
||||||
"\t-c Get title and appid of focused clients\n"
|
|
||||||
"\t-v Get visibility of statusbar\n"
|
|
||||||
"\t-m Get fullscreen status\n"
|
|
||||||
"\t-f Get floating status\n"
|
|
||||||
"\t-x Get focused client geometry\n"
|
|
||||||
"\t-e Get name of last focused layer\n"
|
|
||||||
"\t-k Get current keyboard layout\n"
|
|
||||||
"\t-b Get current keybind mode\n"
|
|
||||||
"\t-A Get scale factor of monitor\n"
|
|
||||||
"\n"
|
|
||||||
"SET OPTIONS (used with -s):\n"
|
|
||||||
"\t-o <output> Select output (monitor)\n"
|
|
||||||
"\t-t <tags> Set selected tags (can be used with [+-^.] "
|
|
||||||
"modifiers)\n"
|
|
||||||
"\t-l <layout> Set current layout\n"
|
|
||||||
"\t-c <tags> Get title and appid of focused client\n"
|
|
||||||
"\t-d <cmd>,<args...> Dispatch internal command (max 5 args)\n");
|
|
||||||
exit(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t main(int32_t 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 == GET || mode == WATCH)
|
|
||||||
oflag = 1;
|
|
||||||
else if (mode == SET)
|
|
||||||
output_name = EARGF(usage());
|
|
||||||
else
|
|
||||||
output_name = ARGF();
|
|
||||||
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());
|
|
||||||
|
|
||||||
dispatch_cmd = dispatch_arg1 = dispatch_arg2 = dispatch_arg3 =
|
|
||||||
dispatch_arg4 = dispatch_arg5 = "";
|
|
||||||
|
|
||||||
char *tokens[6] = {0};
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
while (arg && count < 6) {
|
|
||||||
char *comma = (count < 5) ? strchr(arg, ',') : NULL;
|
|
||||||
if (comma) {
|
|
||||||
*comma = '\0';
|
|
||||||
tokens[count++] = arg;
|
|
||||||
arg = comma + 1;
|
|
||||||
} else {
|
|
||||||
tokens[count++] = arg;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
char *str = tokens[i];
|
|
||||||
while (isspace((unsigned char)*str))
|
|
||||||
str++;
|
|
||||||
if (*str) {
|
|
||||||
char *end = str + strlen(str) - 1;
|
|
||||||
while (end > str && isspace((unsigned char)*end))
|
|
||||||
end--;
|
|
||||||
*(end + 1) = '\0';
|
|
||||||
}
|
|
||||||
tokens[i] = str;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count > 0)
|
|
||||||
dispatch_cmd = tokens[0];
|
|
||||||
if (count > 1)
|
|
||||||
dispatch_arg1 = tokens[1];
|
|
||||||
if (count > 2)
|
|
||||||
dispatch_arg2 = tokens[2];
|
|
||||||
if (count > 3)
|
|
||||||
dispatch_arg3 = tokens[3];
|
|
||||||
if (count > 4)
|
|
||||||
dispatch_arg4 = tokens[4];
|
|
||||||
if (count > 5)
|
|
||||||
dispatch_arg5 = tokens[5];
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
case 'A':
|
|
||||||
Aflag = 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 ||
|
|
||||||
Aflag || dflag))
|
|
||||||
oflag = tflag = lflag = cflag = vflag = mflag = fflag = xflag = eflag =
|
|
||||||
kflag = bflag = Aflag = 1;
|
|
||||||
|
|
||||||
display = wl_display_connect(NULL);
|
// 按行读取并输出,直到连接关闭(get 模式服务端主动 close)或出错
|
||||||
if (!display)
|
char *line = NULL;
|
||||||
die("bad display");
|
size_t len = 0;
|
||||||
|
while (getline(&line, &len, stream) != -1) {
|
||||||
|
printf("%s", line);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
// 检查是否因读取错误退出(而非正常 EOF)
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
if (ferror(stream)) {
|
||||||
|
perror("recv");
|
||||||
|
free(line);
|
||||||
|
fclose(stream); // 关闭 stream 同时关闭 socket
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
wl_display_dispatch(display);
|
free(line);
|
||||||
wl_display_roundtrip(display);
|
fclose(stream);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
if (!dwl_ipc_manager)
|
}
|
||||||
die("bad dwl-ipc protocol");
|
|
||||||
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
if (mode == WATCH)
|
|
||||||
while (wl_display_dispatch(display) != -1)
|
|
||||||
;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
@ -57,4 +57,7 @@ void client_tile_resize(Client *c, struct wlr_box geo, int32_t interact) {
|
||||||
if (!c->isfullscreen && !c->ismaximizescreen) {
|
if (!c->isfullscreen && !c->ismaximizescreen) {
|
||||||
resize(c, geo, interact);
|
resize(c, geo, interact);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint32_t next_client_id = 0;
|
||||||
|
uint32_t generate_client_id(void) { return ++next_client_id; }
|
||||||
|
|
@ -946,6 +946,8 @@ FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value,
|
||||||
} else if (strcmp(func_name, "focusdir") == 0) {
|
} else if (strcmp(func_name, "focusdir") == 0) {
|
||||||
func = focusdir;
|
func = focusdir;
|
||||||
(*arg).i = parse_direction(arg_value);
|
(*arg).i = parse_direction(arg_value);
|
||||||
|
} else if (strcmp(func_name, "focusid") == 0) {
|
||||||
|
func = focusid;
|
||||||
} else if (strcmp(func_name, "incnmaster") == 0) {
|
} else if (strcmp(func_name, "incnmaster") == 0) {
|
||||||
func = incnmaster;
|
func = incnmaster;
|
||||||
(*arg).i = atoi(arg_value);
|
(*arg).i = atoi(arg_value);
|
||||||
|
|
@ -2356,6 +2358,7 @@ bool parse_option(Config *config, char *key, char *value) {
|
||||||
binding->arg.v = NULL;
|
binding->arg.v = NULL;
|
||||||
binding->arg.v2 = NULL;
|
binding->arg.v2 = NULL;
|
||||||
binding->arg.v3 = NULL;
|
binding->arg.v3 = NULL;
|
||||||
|
binding->arg.tc = NULL;
|
||||||
binding->func =
|
binding->func =
|
||||||
parse_func_name(func_name, &binding->arg, arg_value, arg_value2,
|
parse_func_name(func_name, &binding->arg, arg_value, arg_value2,
|
||||||
arg_value3, arg_value4, arg_value5);
|
arg_value3, arg_value4, arg_value5);
|
||||||
|
|
@ -2437,6 +2440,7 @@ bool parse_option(Config *config, char *key, char *value) {
|
||||||
binding->arg.v = NULL;
|
binding->arg.v = NULL;
|
||||||
binding->arg.v2 = NULL;
|
binding->arg.v2 = NULL;
|
||||||
binding->arg.v3 = NULL;
|
binding->arg.v3 = NULL;
|
||||||
|
binding->arg.tc = NULL;
|
||||||
|
|
||||||
// TODO: remove this in next version
|
// TODO: remove this in next version
|
||||||
if (binding->mod == 0 &&
|
if (binding->mod == 0 &&
|
||||||
|
|
@ -2522,6 +2526,7 @@ bool parse_option(Config *config, char *key, char *value) {
|
||||||
binding->arg.v = NULL;
|
binding->arg.v = NULL;
|
||||||
binding->arg.v2 = NULL;
|
binding->arg.v2 = NULL;
|
||||||
binding->arg.v3 = NULL;
|
binding->arg.v3 = NULL;
|
||||||
|
binding->arg.tc = NULL;
|
||||||
binding->func =
|
binding->func =
|
||||||
parse_func_name(func_name, &binding->arg, arg_value, arg_value2,
|
parse_func_name(func_name, &binding->arg, arg_value, arg_value2,
|
||||||
arg_value3, arg_value4, arg_value5);
|
arg_value3, arg_value4, arg_value5);
|
||||||
|
|
@ -2672,6 +2677,7 @@ bool parse_option(Config *config, char *key, char *value) {
|
||||||
binding->arg.v = NULL;
|
binding->arg.v = NULL;
|
||||||
binding->arg.v2 = NULL;
|
binding->arg.v2 = NULL;
|
||||||
binding->arg.v3 = NULL;
|
binding->arg.v3 = NULL;
|
||||||
|
binding->arg.tc = NULL;
|
||||||
binding->func =
|
binding->func =
|
||||||
parse_func_name(func_name, &binding->arg, arg_value, arg_value2,
|
parse_func_name(func_name, &binding->arg, arg_value, arg_value2,
|
||||||
arg_value3, arg_value4, arg_value5);
|
arg_value3, arg_value4, arg_value5);
|
||||||
|
|
|
||||||
|
|
@ -73,4 +73,5 @@ int32_t scroller_stack(const Arg *arg);
|
||||||
int32_t toggle_all_floating(const Arg *arg);
|
int32_t toggle_all_floating(const Arg *arg);
|
||||||
int32_t dwindle_toggle_split_direction(const Arg *arg);
|
int32_t dwindle_toggle_split_direction(const Arg *arg);
|
||||||
int32_t dwindle_split_horizontal(const Arg *arg);
|
int32_t dwindle_split_horizontal(const Arg *arg);
|
||||||
int32_t dwindle_split_vertical(const Arg *arg);
|
int32_t dwindle_split_vertical(const Arg *arg);
|
||||||
|
int32_t focusid(const Arg *arg);
|
||||||
|
|
@ -34,10 +34,8 @@ int32_t bind_to_view(const Arg *arg) {
|
||||||
int32_t chvt(const Arg *arg) {
|
int32_t chvt(const Arg *arg) {
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
|
|
||||||
// prevent the animation to rquest the new frame
|
|
||||||
allow_frame_scheduling = false;
|
allow_frame_scheduling = false;
|
||||||
|
|
||||||
// backup current tag and monitor name
|
|
||||||
if (selmon) {
|
if (selmon) {
|
||||||
chvt_backup_tag = selmon->pertag->curtag;
|
chvt_backup_tag = selmon->pertag->curtag;
|
||||||
strncpy(chvt_backup_selmon, selmon->wlr_output->name,
|
strncpy(chvt_backup_selmon, selmon->wlr_output->name,
|
||||||
|
|
@ -46,19 +44,15 @@ int32_t chvt(const Arg *arg) {
|
||||||
|
|
||||||
wlr_session_change_vt(session, arg->ui);
|
wlr_session_change_vt(session, arg->ui);
|
||||||
|
|
||||||
// wait for DRM device to stabilize and ensure the session state is inactive
|
|
||||||
ts.tv_sec = 0;
|
ts.tv_sec = 0;
|
||||||
ts.tv_nsec = 100000000; // 200ms
|
ts.tv_nsec = 100000000;
|
||||||
nanosleep(&ts, NULL);
|
nanosleep(&ts, NULL);
|
||||||
|
|
||||||
// allow frame scheduling,
|
|
||||||
// because session state is now inactive, rendermon will not enter
|
|
||||||
allow_frame_scheduling = true;
|
allow_frame_scheduling = true;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t create_virtual_output(const Arg *arg) {
|
int32_t create_virtual_output(const Arg *arg) {
|
||||||
|
|
||||||
if (!wlr_backend_is_multi(backend)) {
|
if (!wlr_backend_is_multi(backend)) {
|
||||||
wlr_log(WLR_ERROR, "Expected a multi backend");
|
wlr_log(WLR_ERROR, "Expected a multi backend");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -78,7 +72,6 @@ int32_t create_virtual_output(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t destroy_all_virtual_output(const Arg *arg) {
|
int32_t destroy_all_virtual_output(const Arg *arg) {
|
||||||
|
|
||||||
if (!wlr_backend_is_multi(backend)) {
|
if (!wlr_backend_is_multi(backend)) {
|
||||||
wlr_log(WLR_ERROR, "Expected a multi backend");
|
wlr_log(WLR_ERROR, "Expected a multi backend");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -87,8 +80,6 @@ int32_t destroy_all_virtual_output(const Arg *arg) {
|
||||||
Monitor *m, *tmp;
|
Monitor *m, *tmp;
|
||||||
wl_list_for_each_safe(m, tmp, &mons, link) {
|
wl_list_for_each_safe(m, tmp, &mons, link) {
|
||||||
if (wlr_output_is_headless(m->wlr_output)) {
|
if (wlr_output_is_headless(m->wlr_output)) {
|
||||||
// if(selmon == m)
|
|
||||||
// selmon = NULL;
|
|
||||||
wlr_output_destroy(m->wlr_output);
|
wlr_output_destroy(m->wlr_output);
|
||||||
wlr_log(WLR_INFO, "Virtual output destroyed");
|
wlr_log(WLR_INFO, "Virtual output destroyed");
|
||||||
}
|
}
|
||||||
|
|
@ -104,7 +95,7 @@ int32_t defaultgaps(const Arg *arg) {
|
||||||
int32_t exchange_client(const Arg *arg) {
|
int32_t exchange_client(const Arg *arg) {
|
||||||
if (!selmon)
|
if (!selmon)
|
||||||
return 0;
|
return 0;
|
||||||
Client *c = selmon->sel;
|
Client *c = arg->tc ? arg->tc : selmon->sel;
|
||||||
if (!c || c->isfloating)
|
if (!c || c->isfloating)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
@ -112,7 +103,7 @@ int32_t exchange_client(const Arg *arg) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
Client *tc = direction_select(arg);
|
Client *tc = direction_select(arg);
|
||||||
tc = get_focused_stack_client(tc);
|
tc = get_focused_stack_client(tc, arg->tc);
|
||||||
|
|
||||||
if (!tc)
|
if (!tc)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -125,7 +116,7 @@ int32_t exchange_stack_client(const Arg *arg) {
|
||||||
if (!selmon)
|
if (!selmon)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
Client *c = selmon->sel;
|
Client *c = arg->tc ? arg->tc : selmon->sel;
|
||||||
Client *tc = NULL;
|
Client *tc = NULL;
|
||||||
if (!c || c->isfloating || c->isfullscreen || c->ismaximizescreen)
|
if (!c || c->isfloating || c->isfullscreen || c->ismaximizescreen)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -142,7 +133,7 @@ int32_t exchange_stack_client(const Arg *arg) {
|
||||||
int32_t focusdir(const Arg *arg) {
|
int32_t focusdir(const Arg *arg) {
|
||||||
Client *c = NULL;
|
Client *c = NULL;
|
||||||
c = direction_select(arg);
|
c = direction_select(arg);
|
||||||
c = get_focused_stack_client(c);
|
c = get_focused_stack_client(c, arg->tc);
|
||||||
if (c) {
|
if (c) {
|
||||||
focusclient(c, 1);
|
focusclient(c, 1);
|
||||||
if (config.warpcursor)
|
if (config.warpcursor)
|
||||||
|
|
@ -161,7 +152,6 @@ int32_t focusdir(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t focuslast(const Arg *arg) {
|
int32_t focuslast(const Arg *arg) {
|
||||||
|
|
||||||
Client *c = NULL;
|
Client *c = NULL;
|
||||||
Client *tc = NULL;
|
Client *tc = NULL;
|
||||||
bool begin = false;
|
bool begin = false;
|
||||||
|
|
@ -233,7 +223,7 @@ int32_t focusmon(const Arg *arg) {
|
||||||
if (config.warpcursor) {
|
if (config.warpcursor) {
|
||||||
warp_cursor_to_selmon(selmon);
|
warp_cursor_to_selmon(selmon);
|
||||||
}
|
}
|
||||||
c = focustop(selmon);
|
c = arg->tc ? arg->tc : focustop(selmon);
|
||||||
if (!c) {
|
if (!c) {
|
||||||
selmon->sel = NULL;
|
selmon->sel = NULL;
|
||||||
wlr_seat_pointer_notify_clear_focus(seat);
|
wlr_seat_pointer_notify_clear_focus(seat);
|
||||||
|
|
@ -246,8 +236,7 @@ int32_t focusmon(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t focusstack(const Arg *arg) {
|
int32_t focusstack(const Arg *arg) {
|
||||||
/* Focus the next or previous client (in tiling order) on selmon */
|
Client *sel = arg->tc ? arg->tc : focustop(selmon);
|
||||||
Client *sel = focustop(selmon);
|
|
||||||
Client *tc = NULL;
|
Client *tc = NULL;
|
||||||
|
|
||||||
if (!sel)
|
if (!sel)
|
||||||
|
|
@ -257,7 +246,6 @@ int32_t focusstack(const Arg *arg) {
|
||||||
} else {
|
} else {
|
||||||
tc = get_next_stack_client(sel, true);
|
tc = get_next_stack_client(sel, true);
|
||||||
}
|
}
|
||||||
/* If only one client is visible on selmon, then c == sel */
|
|
||||||
|
|
||||||
if (!tc)
|
if (!tc)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -356,10 +344,7 @@ int32_t setmfact(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t killclient(const Arg *arg) {
|
int32_t killclient(const Arg *arg) {
|
||||||
Client *c = NULL;
|
Client *c = arg->tc ? arg->tc : (selmon ? selmon->sel : NULL);
|
||||||
if (!selmon)
|
|
||||||
return 0;
|
|
||||||
c = selmon->sel;
|
|
||||||
if (c) {
|
if (c) {
|
||||||
pending_kill_client(c);
|
pending_kill_client(c);
|
||||||
}
|
}
|
||||||
|
|
@ -378,7 +363,6 @@ int32_t moveresize(const Arg *arg) {
|
||||||
grabc = NULL;
|
grabc = NULL;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/* Float the window and tell motionnotify to grab it */
|
|
||||||
if (grabc->isfloating == 0 && arg->ui == CurMove) {
|
if (grabc->isfloating == 0 && arg->ui == CurMove) {
|
||||||
grabc->drag_to_tile = true;
|
grabc->drag_to_tile = true;
|
||||||
exit_scroller_stack(grabc);
|
exit_scroller_stack(grabc);
|
||||||
|
|
@ -399,20 +383,16 @@ int32_t moveresize(const Arg *arg) {
|
||||||
|
|
||||||
switch (cursor_mode = arg->ui) {
|
switch (cursor_mode = arg->ui) {
|
||||||
case CurMove:
|
case CurMove:
|
||||||
|
|
||||||
grabcx = cursor->x - grabc->geom.x;
|
grabcx = cursor->x - grabc->geom.x;
|
||||||
grabcy = cursor->y - grabc->geom.y;
|
grabcy = cursor->y - grabc->geom.y;
|
||||||
wlr_cursor_set_xcursor(cursor, cursor_mgr, "grab");
|
wlr_cursor_set_xcursor(cursor, cursor_mgr, "grab");
|
||||||
break;
|
break;
|
||||||
case CurResize:
|
case CurResize:
|
||||||
/* Doesn't work for X11 output - the next absolute motion event
|
|
||||||
* returns the cursor to where it started */
|
|
||||||
if (grabc->isfloating) {
|
if (grabc->isfloating) {
|
||||||
rzcorner = config.drag_corner;
|
rzcorner = config.drag_corner;
|
||||||
grabcx = (int)round(cursor->x);
|
grabcx = (int)round(cursor->x);
|
||||||
grabcy = (int)round(cursor->y);
|
grabcy = (int)round(cursor->y);
|
||||||
if (rzcorner == 4)
|
if (rzcorner == 4)
|
||||||
/* identify the closest corner index */
|
|
||||||
rzcorner = (grabcx - grabc->geom.x <
|
rzcorner = (grabcx - grabc->geom.x <
|
||||||
grabc->geom.x + grabc->geom.width - grabcx
|
grabc->geom.x + grabc->geom.width - grabcx
|
||||||
? 0
|
? 0
|
||||||
|
|
@ -440,14 +420,11 @@ int32_t moveresize(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t movewin(const Arg *arg) {
|
int32_t movewin(const Arg *arg) {
|
||||||
Client *c = NULL;
|
Client *c = arg->tc ? arg->tc : (selmon ? selmon->sel : NULL);
|
||||||
if (!selmon)
|
|
||||||
return 0;
|
|
||||||
c = selmon->sel;
|
|
||||||
if (!c || c->isfullscreen)
|
if (!c || c->isfullscreen)
|
||||||
return 0;
|
return 0;
|
||||||
if (!c->isfloating)
|
if (!c->isfloating)
|
||||||
togglefloating(NULL);
|
setfloating(c, 1);
|
||||||
|
|
||||||
switch (arg->ui) {
|
switch (arg->ui) {
|
||||||
case NUM_TYPE_MINUS:
|
case NUM_TYPE_MINUS:
|
||||||
|
|
@ -485,10 +462,7 @@ int32_t quit(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t resizewin(const Arg *arg) {
|
int32_t resizewin(const Arg *arg) {
|
||||||
Client *c = NULL;
|
Client *c = arg->tc ? arg->tc : (selmon ? selmon->sel : NULL);
|
||||||
if (!selmon)
|
|
||||||
return 0;
|
|
||||||
c = selmon->sel;
|
|
||||||
int32_t offsetx = 0, offsety = 0;
|
int32_t offsetx = 0, offsety = 0;
|
||||||
|
|
||||||
if (!c || c->isfullscreen || c->ismaximizescreen)
|
if (!c || c->isfullscreen || c->ismaximizescreen)
|
||||||
|
|
@ -559,18 +533,17 @@ int32_t resizewin(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t restore_minimized(const Arg *arg) {
|
int32_t restore_minimized(const Arg *arg) {
|
||||||
Client *c = NULL;
|
Client *c = arg->tc ? arg->tc : (selmon ? selmon->sel : NULL);
|
||||||
|
|
||||||
if (selmon && selmon->isoverview)
|
if (selmon && selmon->isoverview)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (selmon && selmon->sel && selmon->sel->is_in_scratchpad &&
|
if (c && c->is_in_scratchpad && c->is_scratchpad_show) {
|
||||||
selmon->sel->is_scratchpad_show) {
|
client_pending_minimized_state(c, 0);
|
||||||
client_pending_minimized_state(selmon->sel, 0);
|
c->is_scratchpad_show = 0;
|
||||||
selmon->sel->is_scratchpad_show = 0;
|
c->is_in_scratchpad = 0;
|
||||||
selmon->sel->is_in_scratchpad = 0;
|
c->isnamedscratchpad = 0;
|
||||||
selmon->sel->isnamedscratchpad = 0;
|
setborder_color(c);
|
||||||
setborder_color(selmon->sel);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -629,11 +602,10 @@ int32_t set_proportion(const Arg *arg) {
|
||||||
!config.scroller_ignore_proportion_single)
|
!config.scroller_ignore_proportion_single)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
Client *tc = selmon->sel;
|
Client *tc = arg->tc ? arg->tc : selmon->sel;
|
||||||
if (!tc)
|
if (!tc)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* 获取堆叠头部客户端 */
|
|
||||||
tc = scroll_get_stack_head_client(tc);
|
tc = scroll_get_stack_head_client(tc);
|
||||||
if (!tc)
|
if (!tc)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -646,12 +618,10 @@ int32_t set_proportion(const Arg *arg) {
|
||||||
if (st)
|
if (st)
|
||||||
node = find_scroller_node(st, tc);
|
node = find_scroller_node(st, tc);
|
||||||
|
|
||||||
/* 同时更新节点和客户端字段 */
|
|
||||||
if (node)
|
if (node)
|
||||||
node->scroller_proportion = arg->f;
|
node->scroller_proportion = arg->f;
|
||||||
tc->scroller_proportion = arg->f;
|
tc->scroller_proportion = arg->f;
|
||||||
|
|
||||||
/* 可选的即时几何更新,arrange 时会重新计算 */
|
|
||||||
uint32_t max_client_width =
|
uint32_t max_client_width =
|
||||||
m->w.width - 2 * config.scroller_structs - config.gappih;
|
m->w.width - 2 * config.scroller_structs - config.gappih;
|
||||||
tc->geom.width = max_client_width * arg->f;
|
tc->geom.width = max_client_width * arg->f;
|
||||||
|
|
@ -675,7 +645,7 @@ int32_t switch_proportion_preset(const Arg *arg) {
|
||||||
!config.scroller_ignore_proportion_single)
|
!config.scroller_ignore_proportion_single)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
Client *tc = selmon->sel;
|
Client *tc = arg->tc ? arg->tc : selmon->sel;
|
||||||
if (!tc)
|
if (!tc)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
@ -691,11 +661,9 @@ int32_t switch_proportion_preset(const Arg *arg) {
|
||||||
if (st)
|
if (st)
|
||||||
node = find_scroller_node(st, tc);
|
node = find_scroller_node(st, tc);
|
||||||
|
|
||||||
/* 优先从节点读取当前比例,以确保切换基于正确的值 */
|
|
||||||
float current_proportion =
|
float current_proportion =
|
||||||
node ? node->scroller_proportion : tc->scroller_proportion;
|
node ? node->scroller_proportion : tc->scroller_proportion;
|
||||||
|
|
||||||
/* 查找预设目标 */
|
|
||||||
for (int32_t i = 0; i < config.scroller_proportion_preset_count; i++) {
|
for (int32_t i = 0; i < config.scroller_proportion_preset_count; i++) {
|
||||||
if (config.scroller_proportion_preset[i] == current_proportion) {
|
if (config.scroller_proportion_preset[i] == current_proportion) {
|
||||||
if (arg->i == NEXT) {
|
if (arg->i == NEXT) {
|
||||||
|
|
@ -720,7 +688,6 @@ int32_t switch_proportion_preset(const Arg *arg) {
|
||||||
if (target_proportion == 0.0f)
|
if (target_proportion == 0.0f)
|
||||||
target_proportion = config.scroller_proportion_preset[0];
|
target_proportion = config.scroller_proportion_preset[0];
|
||||||
|
|
||||||
/* 更新节点和客户端 */
|
|
||||||
if (node)
|
if (node)
|
||||||
node->scroller_proportion = target_proportion;
|
node->scroller_proportion = target_proportion;
|
||||||
tc->scroller_proportion = target_proportion;
|
tc->scroller_proportion = target_proportion;
|
||||||
|
|
@ -739,11 +706,11 @@ int32_t smartmovewin(const Arg *arg) {
|
||||||
int32_t buttom, top, left, right, tar;
|
int32_t buttom, top, left, right, tar;
|
||||||
if (!selmon)
|
if (!selmon)
|
||||||
return 0;
|
return 0;
|
||||||
c = selmon->sel;
|
c = arg->tc ? arg->tc : selmon->sel;
|
||||||
if (!c || c->isfullscreen)
|
if (!c || c->isfullscreen)
|
||||||
return 0;
|
return 0;
|
||||||
if (!c->isfloating)
|
if (!c->isfloating)
|
||||||
setfloating(selmon->sel, true);
|
setfloating(c, true);
|
||||||
nx = c->geom.x;
|
nx = c->geom.x;
|
||||||
ny = c->geom.y;
|
ny = c->geom.y;
|
||||||
|
|
||||||
|
|
@ -842,7 +809,7 @@ int32_t smartresizewin(const Arg *arg) {
|
||||||
int32_t buttom, top, left, right, tar;
|
int32_t buttom, top, left, right, tar;
|
||||||
if (!selmon)
|
if (!selmon)
|
||||||
return 0;
|
return 0;
|
||||||
c = selmon->sel;
|
c = arg->tc ? arg->tc : selmon->sel;
|
||||||
if (!c || c->isfullscreen)
|
if (!c || c->isfullscreen)
|
||||||
return 0;
|
return 0;
|
||||||
if (!c->isfloating)
|
if (!c->isfloating)
|
||||||
|
|
@ -909,10 +876,7 @@ int32_t smartresizewin(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t centerwin(const Arg *arg) {
|
int32_t centerwin(const Arg *arg) {
|
||||||
Client *c = NULL;
|
Client *c = arg->tc ? arg->tc : (selmon ? selmon->sel : NULL);
|
||||||
if (!selmon)
|
|
||||||
return 0;
|
|
||||||
c = selmon->sel;
|
|
||||||
|
|
||||||
if (!c || c->isfullscreen || c->ismaximizescreen)
|
if (!c || c->isfullscreen || c->ismaximizescreen)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -945,7 +909,6 @@ int32_t spawn_shell(const Arg *arg) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (fork() == 0) {
|
if (fork() == 0) {
|
||||||
// 1. 忽略可能导致 coredump 的信号
|
|
||||||
signal(SIGSEGV, SIG_IGN);
|
signal(SIGSEGV, SIG_IGN);
|
||||||
signal(SIGABRT, SIG_IGN);
|
signal(SIGABRT, SIG_IGN);
|
||||||
signal(SIGILL, SIG_IGN);
|
signal(SIGILL, SIG_IGN);
|
||||||
|
|
@ -954,11 +917,8 @@ int32_t spawn_shell(const Arg *arg) {
|
||||||
setsid();
|
setsid();
|
||||||
|
|
||||||
execlp("sh", "sh", "-c", arg->v, (char *)NULL);
|
execlp("sh", "sh", "-c", arg->v, (char *)NULL);
|
||||||
|
|
||||||
// fallback to bash
|
|
||||||
execlp("bash", "bash", "-c", arg->v, (char *)NULL);
|
execlp("bash", "bash", "-c", arg->v, (char *)NULL);
|
||||||
|
|
||||||
// if execlp fails, we should not reach here
|
|
||||||
wlr_log(WLR_DEBUG,
|
wlr_log(WLR_DEBUG,
|
||||||
"mango: failed to execute command '%s' with shell: %s\n",
|
"mango: failed to execute command '%s' with shell: %s\n",
|
||||||
arg->v, strerror(errno));
|
arg->v, strerror(errno));
|
||||||
|
|
@ -972,7 +932,6 @@ int32_t spawn(const Arg *arg) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (fork() == 0) {
|
if (fork() == 0) {
|
||||||
// 1. 忽略可能导致 coredump 的信号
|
|
||||||
signal(SIGSEGV, SIG_IGN);
|
signal(SIGSEGV, SIG_IGN);
|
||||||
signal(SIGABRT, SIG_IGN);
|
signal(SIGABRT, SIG_IGN);
|
||||||
signal(SIGILL, SIG_IGN);
|
signal(SIGILL, SIG_IGN);
|
||||||
|
|
@ -980,20 +939,17 @@ int32_t spawn(const Arg *arg) {
|
||||||
dup2(STDERR_FILENO, STDOUT_FILENO);
|
dup2(STDERR_FILENO, STDOUT_FILENO);
|
||||||
setsid();
|
setsid();
|
||||||
|
|
||||||
// 2. 对整个参数字符串进行单词展开
|
|
||||||
wordexp_t p;
|
wordexp_t p;
|
||||||
if (wordexp(arg->v, &p, 0) != 0) {
|
if (wordexp(arg->v, &p, 0) != 0) {
|
||||||
wlr_log(WLR_DEBUG, "mango: wordexp failed for '%s'\n", arg->v);
|
wlr_log(WLR_DEBUG, "mango: wordexp failed for '%s'\n", arg->v);
|
||||||
_exit(EXIT_FAILURE);
|
_exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 执行命令(p.we_wordv 已经是 argv 数组)
|
|
||||||
execvp(p.we_wordv[0], p.we_wordv);
|
execvp(p.we_wordv[0], p.we_wordv);
|
||||||
|
|
||||||
// 4. execvp 失败时:打印错误,释放 wordexp 资源,然后退出
|
|
||||||
wlr_log(WLR_DEBUG, "mango: execvp '%s' failed: %s\n", p.we_wordv[0],
|
wlr_log(WLR_DEBUG, "mango: execvp '%s' failed: %s\n", p.we_wordv[0],
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
wordfree(&p); // 释放 wordexp 分配的内存
|
wordfree(&p);
|
||||||
_exit(EXIT_FAILURE);
|
_exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1031,7 +987,6 @@ int32_t switch_keyboard_layout(const Arg *arg) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. 获取当前布局和计算下一个布局
|
|
||||||
xkb_layout_index_t current = xkb_state_serialize_layout(
|
xkb_layout_index_t current = xkb_state_serialize_layout(
|
||||||
keyboard->xkb_state, XKB_STATE_LAYOUT_EFFECTIVE);
|
keyboard->xkb_state, XKB_STATE_LAYOUT_EFFECTIVE);
|
||||||
const int32_t num_layouts = xkb_keymap_num_layouts(keyboard->keymap);
|
const int32_t num_layouts = xkb_keymap_num_layouts(keyboard->keymap);
|
||||||
|
|
@ -1047,14 +1002,12 @@ int32_t switch_keyboard_layout(const Arg *arg) {
|
||||||
next = (current + 1) % num_layouts;
|
next = (current + 1) % num_layouts;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. 应用新 keymap
|
|
||||||
uint32_t depressed = keyboard->modifiers.depressed;
|
uint32_t depressed = keyboard->modifiers.depressed;
|
||||||
uint32_t latched = keyboard->modifiers.latched;
|
uint32_t latched = keyboard->modifiers.latched;
|
||||||
uint32_t locked = keyboard->modifiers.locked;
|
uint32_t locked = keyboard->modifiers.locked;
|
||||||
|
|
||||||
wlr_keyboard_notify_modifiers(keyboard, depressed, latched, locked, next);
|
wlr_keyboard_notify_modifiers(keyboard, depressed, latched, locked, next);
|
||||||
|
|
||||||
// 7. 更新 seat
|
|
||||||
wlr_seat_set_keyboard(seat, keyboard);
|
wlr_seat_set_keyboard(seat, keyboard);
|
||||||
wlr_seat_keyboard_notify_modifiers(seat, &keyboard->modifiers);
|
wlr_seat_keyboard_notify_modifiers(seat, &keyboard->modifiers);
|
||||||
|
|
||||||
|
|
@ -1067,7 +1020,6 @@ int32_t switch_keyboard_layout(const Arg *arg) {
|
||||||
struct wlr_keyboard *tkb = (struct wlr_keyboard *)id->device_data;
|
struct wlr_keyboard *tkb = (struct wlr_keyboard *)id->device_data;
|
||||||
|
|
||||||
wlr_keyboard_notify_modifiers(tkb, depressed, latched, locked, next);
|
wlr_keyboard_notify_modifiers(tkb, depressed, latched, locked, next);
|
||||||
// 7. 更新 seat
|
|
||||||
wlr_seat_set_keyboard(seat, tkb);
|
wlr_seat_set_keyboard(seat, tkb);
|
||||||
wlr_seat_keyboard_notify_modifiers(seat, &tkb->modifiers);
|
wlr_seat_keyboard_notify_modifiers(seat, &tkb->modifiers);
|
||||||
}
|
}
|
||||||
|
|
@ -1077,7 +1029,6 @@ int32_t switch_keyboard_layout(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t switch_layout(const Arg *arg) {
|
int32_t switch_layout(const Arg *arg) {
|
||||||
|
|
||||||
int32_t jk, ji;
|
int32_t jk, ji;
|
||||||
char *target_layout_name = NULL;
|
char *target_layout_name = NULL;
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
|
|
@ -1110,13 +1061,8 @@ int32_t switch_layout(const Arg *arg) {
|
||||||
len = MAX(strlen(layouts[ji].name), strlen(target_layout_name));
|
len = MAX(strlen(layouts[ji].name), strlen(target_layout_name));
|
||||||
if (strncmp(layouts[ji].name, target_layout_name, len) == 0) {
|
if (strncmp(layouts[ji].name, target_layout_name, len) == 0) {
|
||||||
selmon->pertag->ltidxs[target_tag] = &layouts[ji];
|
selmon->pertag->ltidxs[target_tag] = &layouts[ji];
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
clear_fullscreen_and_maximized_state(selmon);
|
|
||||||
arrange(selmon, false, false);
|
|
||||||
printstatus();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1137,7 +1083,7 @@ int32_t switch_layout(const Arg *arg) {
|
||||||
int32_t tag(const Arg *arg) {
|
int32_t tag(const Arg *arg) {
|
||||||
if (!selmon)
|
if (!selmon)
|
||||||
return 0;
|
return 0;
|
||||||
Client *target_client = selmon->sel;
|
Client *target_client = arg->tc ? arg->tc : selmon->sel;
|
||||||
tag_client(arg, target_client);
|
tag_client(arg, target_client);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1146,7 +1092,7 @@ int32_t tagmon(const Arg *arg) {
|
||||||
Monitor *m = NULL, *cm = NULL, *oldmon = NULL;
|
Monitor *m = NULL, *cm = NULL, *oldmon = NULL;
|
||||||
if (!selmon)
|
if (!selmon)
|
||||||
return 0;
|
return 0;
|
||||||
Client *c = focustop(selmon);
|
Client *c = arg->tc ? arg->tc : focustop(selmon);
|
||||||
|
|
||||||
if (!c)
|
if (!c)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1180,8 +1126,8 @@ int32_t tagmon(const Arg *arg) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c == selmon->sel) {
|
if (c == oldmon->sel) {
|
||||||
selmon->sel = NULL;
|
oldmon->sel = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
setmon(c, m, newtags, true);
|
setmon(c, m, newtags, true);
|
||||||
|
|
@ -1196,8 +1142,6 @@ int32_t tagmon(const Arg *arg) {
|
||||||
selmon = c->mon;
|
selmon = c->mon;
|
||||||
c->float_geom = setclient_coordinate_center(c, c->mon, c->float_geom, 0, 0);
|
c->float_geom = setclient_coordinate_center(c, c->mon, c->float_geom, 0, 0);
|
||||||
|
|
||||||
// 重新计算居中的坐标
|
|
||||||
// 重新计算居中的坐标
|
|
||||||
if (c->isfloating) {
|
if (c->isfloating) {
|
||||||
c->geom = c->float_geom;
|
c->geom = c->float_geom;
|
||||||
target = get_tags_first_tag(c->tags);
|
target = get_tags_first_tag(c->tags);
|
||||||
|
|
@ -1219,12 +1163,11 @@ int32_t tagmon(const Arg *arg) {
|
||||||
|
|
||||||
int32_t tagsilent(const Arg *arg) {
|
int32_t tagsilent(const Arg *arg) {
|
||||||
Client *fc = NULL;
|
Client *fc = NULL;
|
||||||
Client *target_client = NULL;
|
Client *target_client = arg->tc ? arg->tc : (selmon ? selmon->sel : NULL);
|
||||||
|
|
||||||
if (!selmon || !selmon->sel)
|
if (!target_client)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
target_client = selmon->sel;
|
|
||||||
target_client->tags = arg->ui & TAGMASK;
|
target_client->tags = arg->ui & TAGMASK;
|
||||||
wl_list_for_each(fc, &clients, link) {
|
wl_list_for_each(fc, &clients, link) {
|
||||||
if (fc && fc != target_client && target_client->tags & fc->tags &&
|
if (fc && fc != target_client && target_client->tags & fc->tags &&
|
||||||
|
|
@ -1241,7 +1184,8 @@ int32_t tagtoleft(const Arg *arg) {
|
||||||
if (!selmon)
|
if (!selmon)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (selmon->sel != NULL &&
|
Client *sel = arg->tc ? arg->tc : selmon->sel;
|
||||||
|
if (sel != NULL &&
|
||||||
__builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1) {
|
__builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1) {
|
||||||
uint32_t target = selmon->tagset[selmon->seltags] >> 1;
|
uint32_t target = selmon->tagset[selmon->seltags] >> 1;
|
||||||
|
|
||||||
|
|
@ -1252,7 +1196,8 @@ int32_t tagtoleft(const Arg *arg) {
|
||||||
selmon->carousel_anim_dir = -1;
|
selmon->carousel_anim_dir = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
tag(&(Arg){.ui = target & TAGMASK, .i = arg->i});
|
Arg a = {.ui = target & TAGMASK, .i = arg->i, .tc = sel};
|
||||||
|
tag(&a);
|
||||||
selmon->carousel_anim_dir = 0;
|
selmon->carousel_anim_dir = 0;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1262,7 +1207,8 @@ int32_t tagtoright(const Arg *arg) {
|
||||||
if (!selmon)
|
if (!selmon)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (selmon->sel != NULL &&
|
Client *sel = arg->tc ? arg->tc : selmon->sel;
|
||||||
|
if (sel != NULL &&
|
||||||
__builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1) {
|
__builtin_popcount(selmon->tagset[selmon->seltags] & TAGMASK) == 1) {
|
||||||
uint32_t target = selmon->tagset[selmon->seltags] << 1;
|
uint32_t target = selmon->tagset[selmon->seltags] << 1;
|
||||||
|
|
||||||
|
|
@ -1273,7 +1219,8 @@ int32_t tagtoright(const Arg *arg) {
|
||||||
selmon->carousel_anim_dir = 1;
|
selmon->carousel_anim_dir = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
tag(&(Arg){.ui = target & TAGMASK, .i = arg->i});
|
Arg a = {.ui = target & TAGMASK, .i = arg->i, .tc = sel};
|
||||||
|
tag(&a);
|
||||||
selmon->carousel_anim_dir = 0;
|
selmon->carousel_anim_dir = 0;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1296,7 +1243,6 @@ int32_t toggle_named_scratchpad(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
target_client->isnamedscratchpad = 1;
|
target_client->isnamedscratchpad = 1;
|
||||||
|
|
||||||
apply_named_scratchpad(target_client);
|
apply_named_scratchpad(target_client);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1342,7 +1288,7 @@ int32_t toggle_scratchpad(const Arg *arg) {
|
||||||
int32_t togglefakefullscreen(const Arg *arg) {
|
int32_t togglefakefullscreen(const Arg *arg) {
|
||||||
if (!selmon)
|
if (!selmon)
|
||||||
return 0;
|
return 0;
|
||||||
Client *sel = focustop(selmon);
|
Client *sel = arg->tc ? arg->tc : focustop(selmon);
|
||||||
if (sel)
|
if (sel)
|
||||||
setfakefullscreen(sel, !sel->isfakefullscreen);
|
setfakefullscreen(sel, !sel->isfakefullscreen);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1352,7 +1298,7 @@ int32_t togglefloating(const Arg *arg) {
|
||||||
if (!selmon)
|
if (!selmon)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
Client *sel = focustop(selmon);
|
Client *sel = arg->tc ? arg->tc : focustop(selmon);
|
||||||
|
|
||||||
if (selmon && selmon->isoverview)
|
if (selmon && selmon->isoverview)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -1376,7 +1322,7 @@ int32_t togglefullscreen(const Arg *arg) {
|
||||||
if (!selmon)
|
if (!selmon)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
Client *sel = focustop(selmon);
|
Client *sel = arg->tc ? arg->tc : focustop(selmon);
|
||||||
if (!sel)
|
if (!sel)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
@ -1395,15 +1341,17 @@ int32_t toggleglobal(const Arg *arg) {
|
||||||
if (!selmon)
|
if (!selmon)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!selmon->sel)
|
Client *c = arg->tc ? arg->tc : selmon->sel;
|
||||||
|
if (!c)
|
||||||
return 0;
|
return 0;
|
||||||
if (selmon->sel->is_in_scratchpad) {
|
|
||||||
selmon->sel->is_in_scratchpad = 0;
|
if (c->is_in_scratchpad) {
|
||||||
selmon->sel->is_scratchpad_show = 0;
|
c->is_in_scratchpad = 0;
|
||||||
selmon->sel->isnamedscratchpad = 0;
|
c->is_scratchpad_show = 0;
|
||||||
|
c->isnamedscratchpad = 0;
|
||||||
}
|
}
|
||||||
selmon->sel->isglobal ^= 1;
|
c->isglobal ^= 1;
|
||||||
setborder_color(selmon->sel);
|
setborder_color(c);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1420,7 +1368,7 @@ int32_t togglemaximizescreen(const Arg *arg) {
|
||||||
if (!selmon)
|
if (!selmon)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
Client *sel = focustop(selmon);
|
Client *sel = arg->tc ? arg->tc : focustop(selmon);
|
||||||
if (!sel)
|
if (!sel)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
@ -1441,23 +1389,23 @@ int32_t toggleoverlay(const Arg *arg) {
|
||||||
if (!selmon)
|
if (!selmon)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!selmon->sel || !selmon->sel->mon || selmon->sel->isfullscreen) {
|
Client *c = arg->tc ? arg->tc : selmon->sel;
|
||||||
|
if (!c || !c->mon || c->isfullscreen) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
selmon->sel->isoverlay ^= 1;
|
c->isoverlay ^= 1;
|
||||||
|
|
||||||
if (selmon->sel->isoverlay) {
|
if (c->isoverlay) {
|
||||||
wlr_scene_node_reparent(&selmon->sel->scene->node, layers[LyrOverlay]);
|
wlr_scene_node_reparent(&c->scene->node, layers[LyrOverlay]);
|
||||||
wlr_scene_node_raise_to_top(&selmon->sel->scene->node);
|
wlr_scene_node_raise_to_top(&c->scene->node);
|
||||||
} else if (client_should_overtop(selmon->sel) && selmon->sel->isfloating) {
|
} else if (client_should_overtop(c) && c->isfloating) {
|
||||||
wlr_scene_node_reparent(&selmon->sel->scene->node, layers[LyrTop]);
|
wlr_scene_node_reparent(&c->scene->node, layers[LyrTop]);
|
||||||
} else {
|
} else {
|
||||||
wlr_scene_node_reparent(
|
wlr_scene_node_reparent(&c->scene->node,
|
||||||
&selmon->sel->scene->node,
|
layers[c->isfloating ? LyrTop : LyrTile]);
|
||||||
layers[selmon->sel->isfloating ? LyrTop : LyrTile]);
|
|
||||||
}
|
}
|
||||||
setborder_color(selmon->sel);
|
setborder_color(c);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1466,7 +1414,7 @@ int32_t toggletag(const Arg *arg) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
uint32_t newtags;
|
uint32_t newtags;
|
||||||
Client *sel = focustop(selmon);
|
Client *sel = arg->tc ? arg->tc : focustop(selmon);
|
||||||
if (!sel)
|
if (!sel)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
@ -1649,15 +1597,20 @@ int32_t viewcrossmon(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t tagcrossmon(const Arg *arg) {
|
int32_t tagcrossmon(const Arg *arg) {
|
||||||
if (!selmon || !selmon->sel)
|
if (!selmon)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Client *c = arg->tc ? arg->tc : selmon->sel;
|
||||||
|
if (!c)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (match_monitor_spec(arg->v, selmon)) {
|
if (match_monitor_spec(arg->v, selmon)) {
|
||||||
tag_client(arg, selmon->sel);
|
tag_client(arg, c);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
tagmon(&(Arg){.ui = arg->ui, .i = UNDIR, .v = arg->v});
|
Arg a = {.ui = arg->ui, .i = UNDIR, .v = arg->v, .tc = c};
|
||||||
|
tagmon(&a);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1681,15 +1634,13 @@ int32_t comboview(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t zoom(const Arg *arg) {
|
int32_t zoom(const Arg *arg) {
|
||||||
Client *c = NULL, *sel = focustop(selmon);
|
Client *c = NULL, *sel = arg->tc ? arg->tc : focustop(selmon);
|
||||||
|
|
||||||
if (!sel || !selmon ||
|
if (!sel || !selmon ||
|
||||||
!selmon->pertag->ltidxs[selmon->pertag->curtag]->arrange ||
|
!selmon->pertag->ltidxs[selmon->pertag->curtag]->arrange ||
|
||||||
sel->isfloating)
|
sel->isfloating)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Search for the first tiled window that is not sel, marking sel as
|
|
||||||
* NULL if we pass it along the way */
|
|
||||||
wl_list_for_each(c, &clients,
|
wl_list_for_each(c, &clients,
|
||||||
link) if (VISIBLEON(c, selmon) && !c->isfloating) {
|
link) if (VISIBLEON(c, selmon) && !c->isfloating) {
|
||||||
if (c != sel)
|
if (c != sel)
|
||||||
|
|
@ -1697,12 +1648,9 @@ int32_t zoom(const Arg *arg) {
|
||||||
sel = NULL;
|
sel = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return if no other tiled window was found */
|
|
||||||
if (&c->link == &clients)
|
if (&c->link == &clients)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* If we passed sel, move c to the front; otherwise, move sel to the
|
|
||||||
* front */
|
|
||||||
if (!sel)
|
if (!sel)
|
||||||
sel = c;
|
sel = c;
|
||||||
wl_list_remove(&sel->link);
|
wl_list_remove(&sel->link);
|
||||||
|
|
@ -1727,8 +1675,9 @@ int32_t minimized(const Arg *arg) {
|
||||||
if (selmon && selmon->isoverview)
|
if (selmon && selmon->isoverview)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (selmon->sel && !selmon->sel->isminimized) {
|
Client *c = arg->tc ? arg->tc : selmon->sel;
|
||||||
set_minimized(selmon->sel);
|
if (c && !c->isminimized) {
|
||||||
|
set_minimized(c);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1748,8 +1697,9 @@ int32_t toggleoverview(const Arg *arg) {
|
||||||
if (!selmon)
|
if (!selmon)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (selmon->isoverview && config.ov_tab_mode && arg->i != 1 &&
|
Client *sel = arg->tc ? arg->tc : selmon->sel;
|
||||||
selmon->sel) {
|
|
||||||
|
if (selmon->isoverview && config.ov_tab_mode && arg->i != 1 && sel) {
|
||||||
focusstack(&(Arg){.i = 1});
|
focusstack(&(Arg){.i = 1});
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1774,9 +1724,9 @@ int32_t toggleoverview(const Arg *arg) {
|
||||||
selmon->isoverview ^= 1;
|
selmon->isoverview ^= 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} else if (!selmon->isoverview && selmon->sel) {
|
} else if (!selmon->isoverview && sel) {
|
||||||
target = get_tags_first_tag(selmon->sel->tags);
|
target = get_tags_first_tag(sel->tags);
|
||||||
} else if (!selmon->isoverview && !selmon->sel) {
|
} else if (!selmon->isoverview && !sel) {
|
||||||
target = (1 << (selmon->pertag->prevtag - 1));
|
target = (1 << (selmon->pertag->prevtag - 1));
|
||||||
view(&(Arg){.ui = target}, false);
|
view(&(Arg){.ui = target}, false);
|
||||||
fix_mon_tagset_from_overview(selmon);
|
fix_mon_tagset_from_overview(selmon);
|
||||||
|
|
@ -1784,11 +1734,7 @@ int32_t toggleoverview(const Arg *arg) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 正常视图到overview,退出所有窗口的浮动和全屏状态参与平铺,
|
|
||||||
// overview到正常视图,还原之前退出的浮动和全屏窗口状态
|
|
||||||
if (selmon->isoverview) {
|
if (selmon->isoverview) {
|
||||||
|
|
||||||
// 让游戏窗口无法强制约束鼠标
|
|
||||||
wlr_seat_pointer_clear_focus(seat);
|
wlr_seat_pointer_clear_focus(seat);
|
||||||
wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
|
wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
|
||||||
|
|
||||||
|
|
@ -1879,7 +1825,6 @@ int32_t scroller_apply_stack(Client *c, Client *target_client,
|
||||||
|
|
||||||
struct TagScrollerState *st = ensure_scroller_state(m, tag);
|
struct TagScrollerState *st = ensure_scroller_state(m, tag);
|
||||||
|
|
||||||
/* 获取当前节点 */
|
|
||||||
struct ScrollerStackNode *cnode = find_scroller_node(st, c);
|
struct ScrollerStackNode *cnode = find_scroller_node(st, c);
|
||||||
|
|
||||||
if (!cnode)
|
if (!cnode)
|
||||||
|
|
@ -1888,20 +1833,16 @@ int32_t scroller_apply_stack(Client *c, Client *target_client,
|
||||||
struct ScrollerStackNode *tnode =
|
struct ScrollerStackNode *tnode =
|
||||||
target_client ? find_scroller_node(st, target_client) : NULL;
|
target_client ? find_scroller_node(st, target_client) : NULL;
|
||||||
|
|
||||||
/* 若方向为 UNDIR 且有目标,直接插入到目标尾部 */
|
|
||||||
if (direction == UNDIR && target_client && target_client->mon == c->mon) {
|
if (direction == UNDIR && target_client && target_client->mon == c->mon) {
|
||||||
scroller_insert_stack(c, target_client, false);
|
scroller_insert_stack(c, target_client, false);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 处理从堆叠中移出的情况(方向 LEFT/UP 或 RIGHT/DOWN) */
|
|
||||||
if (cnode->prev_in_stack || cnode->next_in_stack) {
|
if (cnode->prev_in_stack || cnode->next_in_stack) {
|
||||||
struct ScrollerStackNode *move_out_refer_node =
|
struct ScrollerStackNode *move_out_refer_node =
|
||||||
cnode->prev_in_stack ? cnode->prev_in_stack : cnode->next_in_stack;
|
cnode->prev_in_stack ? cnode->prev_in_stack : cnode->next_in_stack;
|
||||||
scroller_node_remove(st, cnode);
|
scroller_node_remove(st, cnode);
|
||||||
|
|
||||||
// 必须先更新,不然里面节点还存着的是cnode的信息,
|
|
||||||
// 会造成stach_head/stack_tail指向的客户端不对
|
|
||||||
update_scroller_state(c->mon);
|
update_scroller_state(c->mon);
|
||||||
|
|
||||||
Client *stack_head =
|
Client *stack_head =
|
||||||
|
|
@ -1928,12 +1869,10 @@ int32_t scroller_apply_stack(Client *c, Client *target_client,
|
||||||
if (!tnode || target_client->mon != c->mon)
|
if (!tnode || target_client->mon != c->mon)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* 找到目标堆叠的尾部节点 */
|
|
||||||
struct ScrollerStackNode *tail = tnode;
|
struct ScrollerStackNode *tail = tnode;
|
||||||
while (tail->next_in_stack)
|
while (tail->next_in_stack)
|
||||||
tail = tail->next_in_stack;
|
tail = tail->next_in_stack;
|
||||||
|
|
||||||
/* 通过封装好的插入函数实现(尾部插入) */
|
|
||||||
scroller_insert_stack(c, tail->client, false);
|
scroller_insert_stack(c, tail->client, false);
|
||||||
|
|
||||||
if (c != tail->client) {
|
if (c != tail->client) {
|
||||||
|
|
@ -1946,7 +1885,7 @@ int32_t scroller_apply_stack(Client *c, Client *target_client,
|
||||||
int32_t scroller_stack(const Arg *arg) {
|
int32_t scroller_stack(const Arg *arg) {
|
||||||
if (!selmon)
|
if (!selmon)
|
||||||
return 0;
|
return 0;
|
||||||
Client *c = selmon->sel;
|
Client *c = arg->tc ? arg->tc : selmon->sel;
|
||||||
if (!c || !c->mon || c->isfloating || !is_scroller_layout(selmon))
|
if (!c || !c->mon || c->isfloating || !is_scroller_layout(selmon))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
@ -1956,15 +1895,18 @@ int32_t scroller_stack(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t toggle_all_floating(const Arg *arg) {
|
int32_t toggle_all_floating(const Arg *arg) {
|
||||||
if (!selmon || !selmon->sel)
|
if (!selmon)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
Client *c = NULL;
|
Client *ref = arg->tc ? arg->tc : selmon->sel;
|
||||||
bool should_floating = !selmon->sel->isfloating;
|
if (!ref)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
bool should_floating = !ref->isfloating;
|
||||||
|
|
||||||
|
Client *c;
|
||||||
wl_list_for_each(c, &clients, link) {
|
wl_list_for_each(c, &clients, link) {
|
||||||
if (VISIBLEON(c, selmon)) {
|
if (VISIBLEON(c, selmon)) {
|
||||||
|
|
||||||
if (c->isfloating && !should_floating) {
|
if (c->isfloating && !should_floating) {
|
||||||
c->old_master_inner_per = 0.0f;
|
c->old_master_inner_per = 0.0f;
|
||||||
c->old_stack_inner_per = 0.0f;
|
c->old_stack_inner_per = 0.0f;
|
||||||
|
|
@ -1980,7 +1922,6 @@ int32_t toggle_all_floating(const Arg *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t dwindle_set_split_direction(Client *c, bool istoggle, bool horizontal) {
|
int32_t dwindle_set_split_direction(Client *c, bool istoggle, bool horizontal) {
|
||||||
|
|
||||||
const Layout *layout = c->mon->pertag->ltidxs[c->mon->pertag->curtag];
|
const Layout *layout = c->mon->pertag->ltidxs[c->mon->pertag->curtag];
|
||||||
|
|
||||||
if (layout->id != DWINDLE)
|
if (layout->id != DWINDLE)
|
||||||
|
|
@ -2005,31 +1946,40 @@ int32_t dwindle_set_split_direction(Client *c, bool istoggle, bool horizontal) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t dwindle_toggle_split_direction(const Arg *arg) {
|
int32_t dwindle_toggle_split_direction(const Arg *arg) {
|
||||||
if (!selmon || !selmon->sel)
|
if (!selmon)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
Client *c = selmon->sel;
|
Client *c = arg->tc ? arg->tc : selmon->sel;
|
||||||
if (!c || !c->mon || c->isfloating)
|
if (!c || !c->mon || c->isfloating)
|
||||||
return 0;
|
return 0;
|
||||||
return dwindle_set_split_direction(selmon->sel, true, false);
|
return dwindle_set_split_direction(c, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t dwindle_split_horizontal(const Arg *arg) {
|
int32_t dwindle_split_horizontal(const Arg *arg) {
|
||||||
if (!selmon || !selmon->sel)
|
if (!selmon)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
Client *c = selmon->sel;
|
Client *c = arg->tc ? arg->tc : selmon->sel;
|
||||||
if (!c || !c->mon || c->isfloating)
|
if (!c || !c->mon || c->isfloating)
|
||||||
return 0;
|
return 0;
|
||||||
return dwindle_set_split_direction(selmon->sel, false, true);
|
return dwindle_set_split_direction(c, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t dwindle_split_vertical(const Arg *arg) {
|
int32_t dwindle_split_vertical(const Arg *arg) {
|
||||||
if (!selmon || !selmon->sel)
|
if (!selmon)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
Client *c = selmon->sel;
|
Client *c = arg->tc ? arg->tc : selmon->sel;
|
||||||
if (!c || !c->mon || c->isfloating)
|
if (!c || !c->mon || c->isfloating)
|
||||||
return 0;
|
return 0;
|
||||||
return dwindle_set_split_direction(selmon->sel, false, false);
|
return dwindle_set_split_direction(c, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t focusid(const Arg *arg) {
|
||||||
|
if (!selmon || !arg->tc)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Client *c = arg->tc;
|
||||||
|
focusclient(c, 1);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -192,7 +192,7 @@ void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output) {
|
||||||
if (wl_resource_get_version(ipc_output->resource) >=
|
if (wl_resource_get_version(ipc_output->resource) >=
|
||||||
ZDWL_IPC_OUTPUT_V2_LAST_LAYER_SINCE_VERSION) {
|
ZDWL_IPC_OUTPUT_V2_LAST_LAYER_SINCE_VERSION) {
|
||||||
zdwl_ipc_output_v2_send_last_layer(ipc_output->resource,
|
zdwl_ipc_output_v2_send_last_layer(ipc_output->resource,
|
||||||
monitor->last_surface_ws_name);
|
monitor->last_open_surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wl_resource_get_version(ipc_output->resource) >=
|
if (wl_resource_get_version(ipc_output->resource) >=
|
||||||
|
|
|
||||||
|
|
@ -452,7 +452,7 @@ Client *find_client_by_direction(Client *tc, const Arg *arg, bool findfloating,
|
||||||
|
|
||||||
Client *direction_select(const Arg *arg) {
|
Client *direction_select(const Arg *arg) {
|
||||||
|
|
||||||
Client *tc = selmon->sel;
|
Client *tc = arg->tc ? arg->tc : selmon->sel;
|
||||||
|
|
||||||
if (!tc)
|
if (!tc)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -593,12 +593,12 @@ bool client_is_in_same_stack(Client *sc, Client *tc, Client *fc) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Client *get_focused_stack_client(Client *sc) {
|
Client *get_focused_stack_client(Client *sc, Client *custom_focus_client) {
|
||||||
if (!sc || sc->isfloating)
|
if (!sc || sc->isfloating)
|
||||||
return sc;
|
return sc;
|
||||||
|
|
||||||
Client *tc = NULL;
|
Client *tc = NULL;
|
||||||
Client *fc = focustop(sc->mon);
|
Client *fc = custom_focus_client ? custom_focus_client : focustop(sc->mon);
|
||||||
|
|
||||||
if (fc->isfloating || sc->isfloating)
|
if (fc->isfloating || sc->isfloating)
|
||||||
return sc;
|
return sc;
|
||||||
|
|
@ -616,4 +616,4 @@ Client *get_focused_stack_client(Client *sc) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sc;
|
return sc;
|
||||||
}
|
}
|
||||||
970
src/ipc/ipc.h
Normal file
970
src/ipc/ipc.h
Normal file
|
|
@ -0,0 +1,970 @@
|
||||||
|
#include <cjson/cJSON.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
enum ipc_watch_type {
|
||||||
|
IPC_WATCH_NONE,
|
||||||
|
IPC_WATCH_MONITOR,
|
||||||
|
IPC_WATCH_CLIENT,
|
||||||
|
IPC_WATCH_TAGS,
|
||||||
|
IPC_WATCH_ALL_MONITORS,
|
||||||
|
IPC_WATCH_ALL_TAGS,
|
||||||
|
IPC_WATCH_ALL_CLIENTS,
|
||||||
|
IPC_WATCH_KEYMODE,
|
||||||
|
IPC_WATCH_KB_LAYOUT,
|
||||||
|
IPC_WATCH_LAST_OPEN_SURFACE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ipc_watch_client {
|
||||||
|
struct wl_list link;
|
||||||
|
int fd;
|
||||||
|
struct wl_event_source *source;
|
||||||
|
enum ipc_watch_type type;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
char name[64];
|
||||||
|
} monitor;
|
||||||
|
struct {
|
||||||
|
uint32_t id;
|
||||||
|
} client;
|
||||||
|
struct {
|
||||||
|
char mon_name[64];
|
||||||
|
} tags;
|
||||||
|
} target;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct wl_list watch_clients;
|
||||||
|
|
||||||
|
struct ipc_client_state {
|
||||||
|
int fd;
|
||||||
|
struct wl_event_source *source;
|
||||||
|
struct wl_event_loop *loop;
|
||||||
|
char *buf;
|
||||||
|
size_t buf_len;
|
||||||
|
size_t buf_cap;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void ipc_remove_watch_client(struct ipc_watch_client *wc);
|
||||||
|
static void ipc_notify_json_to_fd(int fd, cJSON *json);
|
||||||
|
|
||||||
|
/* ---------- 工具函数 ---------- */
|
||||||
|
|
||||||
|
static Monitor *monitor_by_name(const char *name) {
|
||||||
|
Monitor *m;
|
||||||
|
wl_list_for_each(m, &mons, link) {
|
||||||
|
if (strcmp(m->wlr_output->name, name) == 0)
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Client *client_by_id(uint32_t id) {
|
||||||
|
Client *c;
|
||||||
|
wl_list_for_each(c, &clients, link) {
|
||||||
|
if (c->id == id)
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *ipc_get_layout_str(void) {
|
||||||
|
struct wlr_keyboard *keyboard = &kb_group->wlr_group->keyboard;
|
||||||
|
xkb_layout_index_t current = xkb_state_serialize_layout(
|
||||||
|
keyboard->xkb_state, XKB_STATE_LAYOUT_EFFECTIVE);
|
||||||
|
static char layout[32];
|
||||||
|
get_layout_abbr(layout,
|
||||||
|
xkb_keymap_layout_get_name(keyboard->keymap, current));
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
static cJSON *tags_mask_to_array(uint32_t tagmask) {
|
||||||
|
cJSON *arr = cJSON_CreateArray();
|
||||||
|
for (int i = 0; i < LENGTH(tags); i++)
|
||||||
|
if (tagmask & (1 << i))
|
||||||
|
cJSON_AddItemToArray(arr, cJSON_CreateNumber(i + 1));
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static cJSON *build_tags_json(Monitor *m) {
|
||||||
|
cJSON *tags_array = cJSON_CreateArray();
|
||||||
|
Client *c = NULL;
|
||||||
|
for (int tag = 1; tag <= LENGTH(tags); tag++) {
|
||||||
|
int numclients = 0;
|
||||||
|
bool is_active = false, is_urgent = false;
|
||||||
|
uint32_t tagmask = 1 << (tag - 1);
|
||||||
|
if (tagmask & m->tagset[m->seltags])
|
||||||
|
is_active = true;
|
||||||
|
wl_list_for_each(c, &clients, link) {
|
||||||
|
if (c->mon != m)
|
||||||
|
continue;
|
||||||
|
if (!(c->tags & tagmask & TAGMASK))
|
||||||
|
continue;
|
||||||
|
if (c->isurgent)
|
||||||
|
is_urgent = true;
|
||||||
|
numclients++;
|
||||||
|
}
|
||||||
|
cJSON *tag_obj = cJSON_CreateObject();
|
||||||
|
cJSON_AddNumberToObject(tag_obj, "index", tag);
|
||||||
|
cJSON_AddBoolToObject(tag_obj, "is_active", is_active);
|
||||||
|
cJSON_AddBoolToObject(tag_obj, "is_urgent", is_urgent);
|
||||||
|
cJSON_AddStringToObject(tag_obj, "layout",
|
||||||
|
m->pertag->ltidxs[tag]->symbol);
|
||||||
|
cJSON_AddNumberToObject(tag_obj, "client_count", numclients);
|
||||||
|
cJSON_AddItemToArray(tags_array, tag_obj);
|
||||||
|
}
|
||||||
|
return tags_array;
|
||||||
|
}
|
||||||
|
|
||||||
|
static cJSON *monitor_active_client(Monitor *m) {
|
||||||
|
cJSON *obj = cJSON_CreateObject();
|
||||||
|
if (!m->sel) {
|
||||||
|
cJSON_AddNullToObject(obj, "id");
|
||||||
|
cJSON_AddNullToObject(obj, "title");
|
||||||
|
cJSON_AddNullToObject(obj, "appid");
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
Client *c = m->sel;
|
||||||
|
cJSON_AddNumberToObject(obj, "id", c->id);
|
||||||
|
cJSON_AddStringToObject(obj, "title", client_get_title(c));
|
||||||
|
cJSON_AddStringToObject(obj, "appid", client_get_appid(c));
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
static cJSON *monitor_active_tags(Monitor *m) {
|
||||||
|
cJSON *arr = cJSON_CreateArray();
|
||||||
|
uint32_t tagset;
|
||||||
|
if (m->isoverview) {
|
||||||
|
cJSON_AddItemToArray(arr, cJSON_CreateNumber(0));
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
tagset = m->tagset[m->seltags];
|
||||||
|
for (int i = 0; i < LENGTH(tags); i++)
|
||||||
|
if (tagset & (1 << i))
|
||||||
|
cJSON_AddItemToArray(arr, cJSON_CreateNumber(i + 1));
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static cJSON *build_client_json(Client *c) {
|
||||||
|
cJSON *obj = cJSON_CreateObject();
|
||||||
|
cJSON_AddNumberToObject(obj, "id", c->id);
|
||||||
|
cJSON_AddNumberToObject(obj, "pid", c->pid);
|
||||||
|
cJSON_AddStringToObject(obj, "title", client_get_title(c));
|
||||||
|
cJSON_AddStringToObject(obj, "appid", client_get_appid(c));
|
||||||
|
cJSON_AddStringToObject(obj, "monitor", c->mon->wlr_output->name);
|
||||||
|
cJSON_AddItemToObject(obj, "tags", tags_mask_to_array(c->tags));
|
||||||
|
cJSON_AddBoolToObject(obj, "is_focused", c == focustop(c->mon));
|
||||||
|
cJSON_AddBoolToObject(obj, "is_fullscreen", c->isfullscreen);
|
||||||
|
cJSON_AddBoolToObject(obj, "is_floating", c->isfloating);
|
||||||
|
cJSON_AddBoolToObject(obj, "is_maximized", c->ismaximizescreen);
|
||||||
|
cJSON_AddBoolToObject(obj, "is_global", c->isglobal);
|
||||||
|
cJSON_AddBoolToObject(obj, "is_unglobal", c->isunglobal);
|
||||||
|
cJSON_AddBoolToObject(obj, "is_overlay", c->isoverlay);
|
||||||
|
cJSON_AddBoolToObject(obj, "is_fakefullscreen", c->isfakefullscreen);
|
||||||
|
cJSON_AddBoolToObject(obj, "is_minimized", c->isminimized);
|
||||||
|
cJSON_AddBoolToObject(obj, "is_urgent", c->isurgent);
|
||||||
|
cJSON_AddBoolToObject(obj, "is_scratchpad", c->is_in_scratchpad);
|
||||||
|
cJSON_AddBoolToObject(obj, "is_namedscratchpad", c->isnamedscratchpad);
|
||||||
|
cJSON_AddNumberToObject(obj, "x", c->geom.x);
|
||||||
|
cJSON_AddNumberToObject(obj, "y", c->geom.y);
|
||||||
|
cJSON_AddNumberToObject(obj, "width", c->geom.width);
|
||||||
|
cJSON_AddNumberToObject(obj, "height", c->geom.height);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
static cJSON *build_monitor_json(Monitor *m) {
|
||||||
|
cJSON *resp = cJSON_CreateObject();
|
||||||
|
cJSON_AddStringToObject(resp, "name", m->wlr_output->name);
|
||||||
|
cJSON_AddBoolToObject(resp, "active", m == selmon);
|
||||||
|
cJSON_AddNumberToObject(resp, "x", m->m.x);
|
||||||
|
cJSON_AddNumberToObject(resp, "y", m->m.y);
|
||||||
|
cJSON_AddNumberToObject(resp, "width", m->m.width);
|
||||||
|
cJSON_AddNumberToObject(resp, "height", m->m.height);
|
||||||
|
cJSON_AddNumberToObject(resp, "scale", m->wlr_output->scale);
|
||||||
|
cJSON_AddNumberToObject(resp, "layout_index",
|
||||||
|
m->pertag->ltidxs[m->pertag->curtag] - layouts);
|
||||||
|
cJSON_AddStringToObject(resp, "layout_symbol",
|
||||||
|
m->pertag->ltidxs[m->pertag->curtag]->symbol);
|
||||||
|
cJSON_AddStringToObject(resp, "last_open_surface", m->last_open_surface);
|
||||||
|
cJSON_AddItemToObject(resp, "tags", build_tags_json(m));
|
||||||
|
cJSON_AddItemToObject(resp, "active_tags", monitor_active_tags(m));
|
||||||
|
cJSON_AddItemToObject(resp, "active_client", monitor_active_client(m));
|
||||||
|
cJSON_AddItemToObject(resp, "keymode", cJSON_CreateString(keymode.mode));
|
||||||
|
cJSON_AddItemToObject(resp, "keyboardlayout",
|
||||||
|
cJSON_CreateString(ipc_get_layout_str()));
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static cJSON *build_all_tags_entry(Monitor *m) {
|
||||||
|
cJSON *entry = cJSON_CreateObject();
|
||||||
|
cJSON_AddStringToObject(entry, "monitor", m->wlr_output->name);
|
||||||
|
cJSON_AddItemToObject(entry, "tags", build_tags_json(m));
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
static cJSON *build_all_tags_response(void) {
|
||||||
|
cJSON *arr = cJSON_CreateArray();
|
||||||
|
Monitor *m;
|
||||||
|
wl_list_for_each(m, &mons, link)
|
||||||
|
cJSON_AddItemToArray(arr, build_all_tags_entry(m));
|
||||||
|
cJSON *resp = cJSON_CreateObject();
|
||||||
|
cJSON_AddItemToObject(resp, "all_tags", arr);
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static cJSON *build_monitor_tags_response(Monitor *m) {
|
||||||
|
cJSON *resp = cJSON_CreateObject();
|
||||||
|
cJSON_AddStringToObject(resp, "monitor", m->wlr_output->name);
|
||||||
|
cJSON_AddItemToObject(resp, "tags", build_tags_json(m));
|
||||||
|
cJSON_AddItemToObject(resp, "active_tags", monitor_active_tags(m));
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_static_json(int fd, const char *json_str) {
|
||||||
|
size_t len = strlen(json_str);
|
||||||
|
send(fd, json_str, len, MSG_NOSIGNAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- 一次性命令处理 ---------- */
|
||||||
|
static void handle_command(int client_fd, const char *cmd_raw) {
|
||||||
|
cJSON *resp = NULL;
|
||||||
|
char *json_str = NULL;
|
||||||
|
char cmd[1024];
|
||||||
|
|
||||||
|
strncpy(cmd, cmd_raw, sizeof(cmd) - 1);
|
||||||
|
cmd[sizeof(cmd) - 1] = '\0';
|
||||||
|
for (char *p = cmd; *p; p++)
|
||||||
|
if (*p == ',')
|
||||||
|
*p = ' ';
|
||||||
|
|
||||||
|
if (strcmp(cmd, "get version") == 0) {
|
||||||
|
resp = cJSON_CreateObject();
|
||||||
|
cJSON_AddStringToObject(resp, "version", VERSION);
|
||||||
|
} else if (strcmp(cmd, "get keymode") == 0) {
|
||||||
|
resp = cJSON_CreateObject();
|
||||||
|
cJSON_AddStringToObject(resp, "keymode", keymode.mode);
|
||||||
|
} else if (strcmp(cmd, "get keyboardlayout") == 0) {
|
||||||
|
resp = cJSON_CreateObject();
|
||||||
|
cJSON_AddStringToObject(resp, "layout", ipc_get_layout_str());
|
||||||
|
} else if (strncmp(cmd, "get last_open_surface ", 25) == 0) {
|
||||||
|
const char *name = cmd + 25;
|
||||||
|
Monitor *m = monitor_by_name(name);
|
||||||
|
if (!m) {
|
||||||
|
send_static_json(client_fd, "{\"error\":\"monitor not found\"}\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resp = cJSON_CreateObject();
|
||||||
|
cJSON_AddStringToObject(resp, "monitor", m->wlr_output->name);
|
||||||
|
cJSON_AddStringToObject(resp, "last_open_surface",
|
||||||
|
m->last_open_surface);
|
||||||
|
} else if (strncmp(cmd, "get monitor ", 12) == 0) {
|
||||||
|
Monitor *m = monitor_by_name(cmd + 12);
|
||||||
|
if (!m) {
|
||||||
|
send_static_json(client_fd, "{\"error\":\"monitor not found\"}\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resp = build_monitor_json(m);
|
||||||
|
} else if (strncmp(cmd, "get client ", 11) == 0) {
|
||||||
|
Client *c = client_by_id((uint32_t)atoi(cmd + 11));
|
||||||
|
if (!c) {
|
||||||
|
send_static_json(client_fd, "{\"error\":\"client not found\"}\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resp = build_client_json(c);
|
||||||
|
} else if (strncmp(cmd, "get tag ", 8) == 0) {
|
||||||
|
char mon_name[64];
|
||||||
|
int ext_tag_idx;
|
||||||
|
if (sscanf(cmd + 8, "%63s %d", mon_name, &ext_tag_idx) != 2) {
|
||||||
|
send_static_json(
|
||||||
|
client_fd,
|
||||||
|
"{\"error\":\"usage: get tag <monitor> <index>\"}\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int tag_idx = ext_tag_idx - 1;
|
||||||
|
Monitor *m = monitor_by_name(mon_name);
|
||||||
|
if (!m || tag_idx < 0 || tag_idx >= LENGTH(tags)) {
|
||||||
|
send_static_json(client_fd,
|
||||||
|
"{\"error\":\"invalid monitor or tag index\"}\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint32_t tagmask = 1 << tag_idx;
|
||||||
|
int numclients = 0, focused_client = 0;
|
||||||
|
bool is_active = false, is_urgent = false;
|
||||||
|
if (tagmask & m->tagset[m->seltags])
|
||||||
|
is_active = true;
|
||||||
|
|
||||||
|
Client *c, *focused = focustop(m);
|
||||||
|
wl_list_for_each(c, &clients, link) {
|
||||||
|
if (c->mon != m || !(c->tags & tagmask))
|
||||||
|
continue;
|
||||||
|
if (c == focused)
|
||||||
|
focused_client = 1;
|
||||||
|
if (c->isurgent)
|
||||||
|
is_urgent = true;
|
||||||
|
numclients++;
|
||||||
|
}
|
||||||
|
resp = cJSON_CreateObject();
|
||||||
|
cJSON_AddStringToObject(resp, "monitor", m->wlr_output->name);
|
||||||
|
cJSON_AddNumberToObject(resp, "tag_index", ext_tag_idx);
|
||||||
|
cJSON_AddBoolToObject(resp, "is_active", is_active);
|
||||||
|
cJSON_AddBoolToObject(resp, "is_urgent", is_urgent);
|
||||||
|
cJSON_AddNumberToObject(resp, "client_count", numclients);
|
||||||
|
cJSON_AddBoolToObject(resp, "focused_client", focused_client);
|
||||||
|
} else if (strcmp(cmd, "get all-clients") == 0) {
|
||||||
|
cJSON *arr = cJSON_CreateArray();
|
||||||
|
Client *c;
|
||||||
|
wl_list_for_each(c, &clients, link)
|
||||||
|
cJSON_AddItemToArray(arr, build_client_json(c));
|
||||||
|
resp = cJSON_CreateObject();
|
||||||
|
cJSON_AddItemToObject(resp, "clients", arr);
|
||||||
|
} else if (strcmp(cmd, "get all-monitors") == 0) {
|
||||||
|
cJSON *arr = cJSON_CreateArray();
|
||||||
|
Monitor *m;
|
||||||
|
wl_list_for_each(m, &mons, link)
|
||||||
|
cJSON_AddItemToArray(arr, build_monitor_json(m));
|
||||||
|
resp = cJSON_CreateObject();
|
||||||
|
cJSON_AddItemToObject(resp, "monitors", arr);
|
||||||
|
} else if (strcmp(cmd, "get all-tags") == 0) {
|
||||||
|
resp = build_all_tags_response();
|
||||||
|
} else if (strncmp(cmd, "get tags ", 9) == 0) {
|
||||||
|
Monitor *m = monitor_by_name(cmd + 9);
|
||||||
|
if (!m) {
|
||||||
|
send_static_json(client_fd, "{\"error\":\"monitor not found\"}\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resp = build_monitor_tags_response(m);
|
||||||
|
} else if (strncmp(cmd, "dispatch ", 9) == 0) {
|
||||||
|
char *dispatch_copy = strdup(cmd_raw + 9);
|
||||||
|
char *out = dispatch_copy, *ptr = dispatch_copy;
|
||||||
|
int client_id = -1;
|
||||||
|
while (*ptr) {
|
||||||
|
while (*ptr == ' ' || *ptr == '\t')
|
||||||
|
*out++ = *ptr++;
|
||||||
|
if (strncmp(ptr, "client,", 7) == 0) {
|
||||||
|
char *end;
|
||||||
|
long id = strtol(ptr + 7, &end, 10);
|
||||||
|
if (id > 0 && end > ptr + 7 && (*end == '\0' || *end == ',')) {
|
||||||
|
client_id = (int)id;
|
||||||
|
ptr = end;
|
||||||
|
if (*ptr == ',')
|
||||||
|
ptr++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*out++ = *ptr++;
|
||||||
|
}
|
||||||
|
*out = '\0';
|
||||||
|
|
||||||
|
char *tokens[6] = {NULL};
|
||||||
|
int token_count = 0;
|
||||||
|
char *saveptr;
|
||||||
|
char *token = strtok_r(dispatch_copy, ",", &saveptr);
|
||||||
|
while (token && token_count < 6) {
|
||||||
|
while (*token == ' ' || *token == '\t')
|
||||||
|
token++;
|
||||||
|
char *end = token + strlen(token) - 1;
|
||||||
|
while (end >= token && (*end == ' ' || *end == '\t'))
|
||||||
|
*end-- = '\0';
|
||||||
|
tokens[token_count++] = token;
|
||||||
|
token = strtok_r(NULL, ",", &saveptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
Arg arg = {0};
|
||||||
|
int32_t (*func)(const Arg *) =
|
||||||
|
parse_func_name(token_count > 0 ? tokens[0] : "", &arg,
|
||||||
|
token_count > 1 ? tokens[1] : NULL,
|
||||||
|
token_count > 2 ? tokens[2] : NULL,
|
||||||
|
token_count > 3 ? tokens[3] : NULL,
|
||||||
|
token_count > 4 ? tokens[4] : NULL,
|
||||||
|
token_count > 5 ? tokens[5] : NULL);
|
||||||
|
|
||||||
|
if (func && client_id > 0)
|
||||||
|
arg.tc = client_by_id((uint32_t)client_id);
|
||||||
|
|
||||||
|
if (func) {
|
||||||
|
func(&arg);
|
||||||
|
send_static_json(client_fd, "{\"success\":true}\n");
|
||||||
|
} else {
|
||||||
|
send_static_json(client_fd, "{\"error\":\"unknown function\"}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg.v)
|
||||||
|
free(arg.v);
|
||||||
|
if (arg.v2)
|
||||||
|
free(arg.v2);
|
||||||
|
if (arg.v3)
|
||||||
|
free(arg.v3);
|
||||||
|
free(dispatch_copy);
|
||||||
|
return; // Fast path exit
|
||||||
|
} else {
|
||||||
|
send_static_json(client_fd, "{\"error\":\"unknown command\"}\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resp) {
|
||||||
|
json_str = cJSON_PrintUnformatted(resp);
|
||||||
|
if (json_str) {
|
||||||
|
size_t len = strlen(json_str);
|
||||||
|
char *msg = malloc(len + 2);
|
||||||
|
if (msg) {
|
||||||
|
snprintf(msg, len + 2, "%s\n", json_str);
|
||||||
|
send(client_fd, msg, len + 1, MSG_NOSIGNAL);
|
||||||
|
free(msg);
|
||||||
|
}
|
||||||
|
free(json_str);
|
||||||
|
}
|
||||||
|
cJSON_Delete(resp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Watch 模式支持 ---------- */
|
||||||
|
static void ipc_notify_json_to_fd(int fd, cJSON *json) {
|
||||||
|
char *str = cJSON_PrintUnformatted(json);
|
||||||
|
if (!str)
|
||||||
|
return;
|
||||||
|
size_t len = strlen(str);
|
||||||
|
char *msg = malloc(len + 2);
|
||||||
|
if (!msg) {
|
||||||
|
free(str);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
snprintf(msg, len + 2, "%s\n", str);
|
||||||
|
if (send(fd, msg, len + 1, MSG_NOSIGNAL) < 0) {
|
||||||
|
struct ipc_watch_client *wc, *tmp;
|
||||||
|
wl_list_for_each_safe(wc, tmp, &watch_clients, link) {
|
||||||
|
if (wc->fd == fd) {
|
||||||
|
ipc_remove_watch_client(wc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(msg);
|
||||||
|
free(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ipc_remove_watch_client(struct ipc_watch_client *wc) {
|
||||||
|
wl_list_remove(&wc->link);
|
||||||
|
wl_event_source_remove(wc->source);
|
||||||
|
close(wc->fd);
|
||||||
|
free(wc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ipc_watch_data_handler(int fd, uint32_t mask, void *data) {
|
||||||
|
struct ipc_watch_client *wc = data;
|
||||||
|
if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) {
|
||||||
|
ipc_remove_watch_client(wc);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (mask & WL_EVENT_READABLE) {
|
||||||
|
char buf[64];
|
||||||
|
ssize_t n = recv(fd, buf, sizeof(buf), MSG_DONTWAIT);
|
||||||
|
if (n == 0 || (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK)) {
|
||||||
|
ipc_remove_watch_client(wc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool handle_watch_command(int fd, const char *cmd,
|
||||||
|
struct ipc_client_state *client) {
|
||||||
|
enum ipc_watch_type type = IPC_WATCH_NONE;
|
||||||
|
const char *arg = NULL;
|
||||||
|
uint32_t client_id = 0;
|
||||||
|
|
||||||
|
if (strncmp(cmd, "watch monitor ", 14) == 0) {
|
||||||
|
type = IPC_WATCH_MONITOR;
|
||||||
|
arg = cmd + 14;
|
||||||
|
} else if (strncmp(cmd, "watch client ", 13) == 0) {
|
||||||
|
type = IPC_WATCH_CLIENT;
|
||||||
|
client_id = (uint32_t)atoi(cmd + 13);
|
||||||
|
} else if (strncmp(cmd, "watch tags ", 11) == 0) {
|
||||||
|
type = IPC_WATCH_TAGS;
|
||||||
|
arg = cmd + 11;
|
||||||
|
} else if (strcmp(cmd, "watch all-monitors") == 0) {
|
||||||
|
type = IPC_WATCH_ALL_MONITORS;
|
||||||
|
} else if (strcmp(cmd, "watch all-tags") == 0) {
|
||||||
|
type = IPC_WATCH_ALL_TAGS;
|
||||||
|
} else if (strcmp(cmd, "watch all-clients") == 0) {
|
||||||
|
type = IPC_WATCH_ALL_CLIENTS;
|
||||||
|
} else if (strcmp(cmd, "watch keymode") == 0) {
|
||||||
|
type = IPC_WATCH_KEYMODE;
|
||||||
|
} else if (strcmp(cmd, "watch keyboardlayout") == 0) {
|
||||||
|
type = IPC_WATCH_KB_LAYOUT;
|
||||||
|
} else if (strncmp(cmd, "watch last_open_surface ", 27) == 0) {
|
||||||
|
type = IPC_WATCH_LAST_OPEN_SURFACE;
|
||||||
|
arg = cmd + 27;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == IPC_WATCH_NONE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
struct ipc_watch_client *wc = calloc(1, sizeof(*wc));
|
||||||
|
wc->fd = fd;
|
||||||
|
wc->type = type;
|
||||||
|
|
||||||
|
if ((type == IPC_WATCH_MONITOR || type == IPC_WATCH_LAST_OPEN_SURFACE) &&
|
||||||
|
arg)
|
||||||
|
snprintf(wc->target.monitor.name, sizeof(wc->target.monitor.name), "%s",
|
||||||
|
arg);
|
||||||
|
else if (type == IPC_WATCH_TAGS && arg)
|
||||||
|
snprintf(wc->target.tags.mon_name, sizeof(wc->target.tags.mon_name),
|
||||||
|
"%s", arg);
|
||||||
|
else if (type == IPC_WATCH_CLIENT)
|
||||||
|
wc->target.client.id = client_id;
|
||||||
|
|
||||||
|
wl_event_source_remove(client->source);
|
||||||
|
wc->source = wl_event_loop_add_fd(
|
||||||
|
client->loop, fd, WL_EVENT_READABLE | WL_EVENT_HANGUP | WL_EVENT_ERROR,
|
||||||
|
ipc_watch_data_handler, wc);
|
||||||
|
wl_list_insert(&watch_clients, &wc->link);
|
||||||
|
|
||||||
|
/* 推送初始状态 */
|
||||||
|
cJSON *json = NULL;
|
||||||
|
switch (type) {
|
||||||
|
case IPC_WATCH_MONITOR: {
|
||||||
|
Monitor *m = monitor_by_name(arg);
|
||||||
|
if (m)
|
||||||
|
json = build_monitor_json(m);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IPC_WATCH_LAST_OPEN_SURFACE: {
|
||||||
|
Monitor *m = monitor_by_name(arg);
|
||||||
|
if (m) {
|
||||||
|
json = cJSON_CreateObject();
|
||||||
|
cJSON_AddStringToObject(json, "monitor", m->wlr_output->name);
|
||||||
|
cJSON_AddStringToObject(json, "last_open_surface",
|
||||||
|
m->last_open_surface);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IPC_WATCH_CLIENT: {
|
||||||
|
Client *c = client_by_id(client_id);
|
||||||
|
if (c)
|
||||||
|
json = build_client_json(c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IPC_WATCH_TAGS: {
|
||||||
|
Monitor *m = monitor_by_name(arg);
|
||||||
|
if (m)
|
||||||
|
json = build_monitor_tags_response(m);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IPC_WATCH_ALL_MONITORS: {
|
||||||
|
cJSON *arr = cJSON_CreateArray();
|
||||||
|
Monitor *m;
|
||||||
|
wl_list_for_each(m, &mons, link)
|
||||||
|
cJSON_AddItemToArray(arr, build_monitor_json(m));
|
||||||
|
json = cJSON_CreateObject();
|
||||||
|
cJSON_AddItemToObject(json, "monitors", arr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IPC_WATCH_ALL_TAGS: {
|
||||||
|
json = build_all_tags_response();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IPC_WATCH_ALL_CLIENTS: {
|
||||||
|
cJSON *arr = cJSON_CreateArray();
|
||||||
|
Client *c;
|
||||||
|
wl_list_for_each(c, &clients, link)
|
||||||
|
cJSON_AddItemToArray(arr, build_client_json(c));
|
||||||
|
json = cJSON_CreateObject();
|
||||||
|
cJSON_AddItemToObject(json, "clients", arr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IPC_WATCH_KEYMODE: {
|
||||||
|
json = cJSON_CreateObject();
|
||||||
|
cJSON_AddStringToObject(json, "keymode", keymode.mode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IPC_WATCH_KB_LAYOUT: {
|
||||||
|
json = cJSON_CreateObject();
|
||||||
|
cJSON_AddStringToObject(json, "layout", ipc_get_layout_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json) {
|
||||||
|
ipc_notify_json_to_fd(fd, json);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(client->buf);
|
||||||
|
free(client);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Socket 事件处理 ---------- */
|
||||||
|
static int ipc_handle_client_data(int fd, uint32_t mask, void *data) {
|
||||||
|
struct ipc_client_state *client = data;
|
||||||
|
if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (mask & WL_EVENT_READABLE) {
|
||||||
|
size_t available = client->buf_cap - client->buf_len;
|
||||||
|
if (available < 4096) {
|
||||||
|
size_t new_cap = client->buf_cap ? client->buf_cap * 2 : 8192;
|
||||||
|
char *new_buf = realloc(client->buf, new_cap);
|
||||||
|
if (!new_buf) {
|
||||||
|
wlr_log(WLR_ERROR, "IPC: out of memory");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
client->buf = new_buf;
|
||||||
|
client->buf_cap = new_cap;
|
||||||
|
available = client->buf_cap - client->buf_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 直接读取到 client->buf 尾部,跳过临时数组
|
||||||
|
ssize_t n = recv(fd, client->buf + client->buf_len, available - 1,
|
||||||
|
MSG_DONTWAIT);
|
||||||
|
if (n <= 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
client->buf_len += n;
|
||||||
|
client->buf[client->buf_len] = '\0';
|
||||||
|
|
||||||
|
char *nl = memchr(client->buf, '\n', client->buf_len);
|
||||||
|
if (!nl) {
|
||||||
|
if (client->buf_len > 1024 * 1024)
|
||||||
|
goto cleanup; // 防御过长命令
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*nl = '\0';
|
||||||
|
char *cmd = client->buf;
|
||||||
|
|
||||||
|
bool is_watch = handle_watch_command(fd, cmd, client);
|
||||||
|
if (is_watch)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
handle_command(fd, cmd);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
close(client->fd);
|
||||||
|
wl_event_source_remove(client->source);
|
||||||
|
free(client->buf);
|
||||||
|
free(client);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ipc_handle_connection(int fd, uint32_t mask, void *data) {
|
||||||
|
struct wl_event_loop *loop = data;
|
||||||
|
int client_fd = accept(fd, NULL, NULL);
|
||||||
|
if (client_fd < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int flags = fcntl(client_fd, F_GETFL, 0);
|
||||||
|
fcntl(client_fd, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
|
||||||
|
struct ipc_client_state *client = calloc(1, sizeof(*client));
|
||||||
|
client->fd = client_fd;
|
||||||
|
client->loop = loop;
|
||||||
|
client->source = wl_event_loop_add_fd(
|
||||||
|
loop, client_fd, WL_EVENT_READABLE | WL_EVENT_HANGUP | WL_EVENT_ERROR,
|
||||||
|
ipc_handle_client_data, client);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- 外部通知接口 ---------- */
|
||||||
|
|
||||||
|
void ipc_notify_monitor(Monitor *m) {
|
||||||
|
char *json_str = NULL;
|
||||||
|
size_t len = 0;
|
||||||
|
struct ipc_watch_client *wc, *tmp;
|
||||||
|
wl_list_for_each_safe(wc, tmp, &watch_clients, link) {
|
||||||
|
if (wc->type == IPC_WATCH_MONITOR &&
|
||||||
|
strcmp(m->wlr_output->name, wc->target.monitor.name) == 0) {
|
||||||
|
if (!json_str) {
|
||||||
|
cJSON *json = build_monitor_json(m);
|
||||||
|
char *raw = cJSON_PrintUnformatted(json);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
if (!raw)
|
||||||
|
return;
|
||||||
|
len = strlen(raw);
|
||||||
|
json_str = malloc(len + 2);
|
||||||
|
snprintf(json_str, len + 2, "%s\n", raw);
|
||||||
|
free(raw);
|
||||||
|
}
|
||||||
|
if (send(wc->fd, json_str, len + 1, MSG_NOSIGNAL) < 0)
|
||||||
|
ipc_remove_watch_client(wc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (json_str)
|
||||||
|
free(json_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ipc_notify_last_surface_ws_name(Monitor *m) {
|
||||||
|
char *json_str = NULL;
|
||||||
|
size_t len = 0;
|
||||||
|
struct ipc_watch_client *wc, *tmp;
|
||||||
|
wl_list_for_each_safe(wc, tmp, &watch_clients, link) {
|
||||||
|
if (wc->type == IPC_WATCH_LAST_OPEN_SURFACE &&
|
||||||
|
strcmp(m->wlr_output->name, wc->target.monitor.name) == 0) {
|
||||||
|
if (!json_str) {
|
||||||
|
cJSON *json = cJSON_CreateObject();
|
||||||
|
cJSON_AddStringToObject(json, "monitor", m->wlr_output->name);
|
||||||
|
cJSON_AddStringToObject(json, "last_open_surface",
|
||||||
|
m->last_open_surface);
|
||||||
|
char *raw = cJSON_PrintUnformatted(json);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
if (!raw)
|
||||||
|
return;
|
||||||
|
len = strlen(raw);
|
||||||
|
json_str = malloc(len + 2);
|
||||||
|
snprintf(json_str, len + 2, "%s\n", raw);
|
||||||
|
free(raw);
|
||||||
|
}
|
||||||
|
if (send(wc->fd, json_str, len + 1, MSG_NOSIGNAL) < 0)
|
||||||
|
ipc_remove_watch_client(wc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (json_str)
|
||||||
|
free(json_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ipc_notify_client(Client *c) {
|
||||||
|
char *json_str = NULL;
|
||||||
|
size_t len = 0;
|
||||||
|
struct ipc_watch_client *wc, *tmp;
|
||||||
|
wl_list_for_each_safe(wc, tmp, &watch_clients, link) {
|
||||||
|
if (wc->type == IPC_WATCH_CLIENT && c->id == wc->target.client.id) {
|
||||||
|
if (!json_str) {
|
||||||
|
cJSON *json = build_client_json(c);
|
||||||
|
char *raw = cJSON_PrintUnformatted(json);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
if (!raw)
|
||||||
|
return;
|
||||||
|
len = strlen(raw);
|
||||||
|
json_str = malloc(len + 2);
|
||||||
|
snprintf(json_str, len + 2, "%s\n", raw);
|
||||||
|
free(raw);
|
||||||
|
}
|
||||||
|
if (send(wc->fd, json_str, len + 1, MSG_NOSIGNAL) < 0)
|
||||||
|
ipc_remove_watch_client(wc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (json_str)
|
||||||
|
free(json_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ipc_notify_tags(Monitor *m) {
|
||||||
|
char *json_str = NULL;
|
||||||
|
size_t len = 0;
|
||||||
|
struct ipc_watch_client *wc, *tmp;
|
||||||
|
wl_list_for_each_safe(wc, tmp, &watch_clients, link) {
|
||||||
|
if (wc->type == IPC_WATCH_TAGS &&
|
||||||
|
strcmp(m->wlr_output->name, wc->target.tags.mon_name) == 0) {
|
||||||
|
if (!json_str) {
|
||||||
|
cJSON *json = build_monitor_tags_response(m);
|
||||||
|
char *raw = cJSON_PrintUnformatted(json);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
if (!raw)
|
||||||
|
return;
|
||||||
|
len = strlen(raw);
|
||||||
|
json_str = malloc(len + 2);
|
||||||
|
snprintf(json_str, len + 2, "%s\n", raw);
|
||||||
|
free(raw);
|
||||||
|
}
|
||||||
|
if (send(wc->fd, json_str, len + 1, MSG_NOSIGNAL) < 0)
|
||||||
|
ipc_remove_watch_client(wc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (json_str)
|
||||||
|
free(json_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ipc_notify_all_monitors(void) {
|
||||||
|
char *json_str = NULL;
|
||||||
|
size_t len = 0;
|
||||||
|
struct ipc_watch_client *wc, *tmp;
|
||||||
|
wl_list_for_each_safe(wc, tmp, &watch_clients, link) {
|
||||||
|
if (wc->type == IPC_WATCH_ALL_MONITORS) {
|
||||||
|
if (!json_str) {
|
||||||
|
cJSON *arr = cJSON_CreateArray();
|
||||||
|
Monitor *m;
|
||||||
|
wl_list_for_each(m, &mons, link)
|
||||||
|
cJSON_AddItemToArray(arr, build_monitor_json(m));
|
||||||
|
cJSON *json = cJSON_CreateObject();
|
||||||
|
cJSON_AddItemToObject(json, "monitors", arr);
|
||||||
|
char *raw = cJSON_PrintUnformatted(json);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
if (!raw)
|
||||||
|
return;
|
||||||
|
len = strlen(raw);
|
||||||
|
json_str = malloc(len + 2);
|
||||||
|
snprintf(json_str, len + 2, "%s\n", raw);
|
||||||
|
free(raw);
|
||||||
|
}
|
||||||
|
if (send(wc->fd, json_str, len + 1, MSG_NOSIGNAL) < 0)
|
||||||
|
ipc_remove_watch_client(wc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (json_str)
|
||||||
|
free(json_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ipc_notify_all_clients(void) {
|
||||||
|
char *json_str = NULL;
|
||||||
|
size_t len = 0;
|
||||||
|
struct ipc_watch_client *wc, *tmp;
|
||||||
|
wl_list_for_each_safe(wc, tmp, &watch_clients, link) {
|
||||||
|
if (wc->type == IPC_WATCH_ALL_CLIENTS) {
|
||||||
|
if (!json_str) {
|
||||||
|
cJSON *arr = cJSON_CreateArray();
|
||||||
|
Client *c;
|
||||||
|
wl_list_for_each(c, &clients, link)
|
||||||
|
cJSON_AddItemToArray(arr, build_client_json(c));
|
||||||
|
cJSON *json = cJSON_CreateObject();
|
||||||
|
cJSON_AddItemToObject(json, "clients", arr);
|
||||||
|
char *raw = cJSON_PrintUnformatted(json);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
if (!raw)
|
||||||
|
return;
|
||||||
|
len = strlen(raw);
|
||||||
|
json_str = malloc(len + 2);
|
||||||
|
snprintf(json_str, len + 2, "%s\n", raw);
|
||||||
|
free(raw);
|
||||||
|
}
|
||||||
|
if (send(wc->fd, json_str, len + 1, MSG_NOSIGNAL) < 0)
|
||||||
|
ipc_remove_watch_client(wc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (json_str)
|
||||||
|
free(json_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ipc_notify_all_tags(void) {
|
||||||
|
char *json_str = NULL;
|
||||||
|
size_t len = 0;
|
||||||
|
struct ipc_watch_client *wc, *tmp;
|
||||||
|
wl_list_for_each_safe(wc, tmp, &watch_clients, link) {
|
||||||
|
if (wc->type == IPC_WATCH_ALL_TAGS) {
|
||||||
|
if (!json_str) {
|
||||||
|
cJSON *json = build_all_tags_response();
|
||||||
|
char *raw = cJSON_PrintUnformatted(json);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
if (!raw)
|
||||||
|
return;
|
||||||
|
len = strlen(raw);
|
||||||
|
json_str = malloc(len + 2);
|
||||||
|
snprintf(json_str, len + 2, "%s\n", raw);
|
||||||
|
free(raw);
|
||||||
|
}
|
||||||
|
if (send(wc->fd, json_str, len + 1, MSG_NOSIGNAL) < 0)
|
||||||
|
ipc_remove_watch_client(wc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (json_str)
|
||||||
|
free(json_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ipc_notify_keymode(void) {
|
||||||
|
char *json_str = NULL;
|
||||||
|
size_t len = 0;
|
||||||
|
struct ipc_watch_client *wc, *tmp;
|
||||||
|
wl_list_for_each_safe(wc, tmp, &watch_clients, link) {
|
||||||
|
if (wc->type == IPC_WATCH_KEYMODE) {
|
||||||
|
if (!json_str) {
|
||||||
|
cJSON *json = cJSON_CreateObject();
|
||||||
|
cJSON_AddStringToObject(json, "keymode", keymode.mode);
|
||||||
|
char *raw = cJSON_PrintUnformatted(json);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
if (!raw)
|
||||||
|
return;
|
||||||
|
len = strlen(raw);
|
||||||
|
json_str = malloc(len + 2);
|
||||||
|
snprintf(json_str, len + 2, "%s\n", raw);
|
||||||
|
free(raw);
|
||||||
|
}
|
||||||
|
if (send(wc->fd, json_str, len + 1, MSG_NOSIGNAL) < 0)
|
||||||
|
ipc_remove_watch_client(wc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (json_str)
|
||||||
|
free(json_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ipc_notify_kb_layout(void) {
|
||||||
|
char *json_str = NULL;
|
||||||
|
size_t len = 0;
|
||||||
|
struct ipc_watch_client *wc, *tmp;
|
||||||
|
wl_list_for_each_safe(wc, tmp, &watch_clients, link) {
|
||||||
|
if (wc->type == IPC_WATCH_KB_LAYOUT) {
|
||||||
|
if (!json_str) {
|
||||||
|
cJSON *json = cJSON_CreateObject();
|
||||||
|
cJSON_AddStringToObject(json, "layout", ipc_get_layout_str());
|
||||||
|
char *raw = cJSON_PrintUnformatted(json);
|
||||||
|
cJSON_Delete(json);
|
||||||
|
if (!raw)
|
||||||
|
return;
|
||||||
|
len = strlen(raw);
|
||||||
|
json_str = malloc(len + 2);
|
||||||
|
snprintf(json_str, len + 2, "%s\n", raw);
|
||||||
|
free(raw);
|
||||||
|
}
|
||||||
|
if (send(wc->fd, json_str, len + 1, MSG_NOSIGNAL) < 0)
|
||||||
|
ipc_remove_watch_client(wc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (json_str)
|
||||||
|
free(json_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- 初始化与清理 ---------- */
|
||||||
|
static int ipc_sock_fd = -1;
|
||||||
|
static struct wl_event_source *ipc_event_source = NULL;
|
||||||
|
static char ipc_socket_path[256];
|
||||||
|
|
||||||
|
void ipc_init(struct wl_event_loop *event_loop) {
|
||||||
|
wl_list_init(&watch_clients);
|
||||||
|
|
||||||
|
const char *xdg_runtime = getenv("XDG_RUNTIME_DIR");
|
||||||
|
if (!xdg_runtime)
|
||||||
|
return;
|
||||||
|
|
||||||
|
snprintf(ipc_socket_path, sizeof(ipc_socket_path), "%s/mango-%d.sock",
|
||||||
|
xdg_runtime, getpid());
|
||||||
|
|
||||||
|
ipc_sock_fd =
|
||||||
|
socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
|
||||||
|
if (ipc_sock_fd < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct sockaddr_un addr = {.sun_family = AF_UNIX};
|
||||||
|
strncpy(addr.sun_path, ipc_socket_path, sizeof(addr.sun_path) - 1);
|
||||||
|
|
||||||
|
unlink(ipc_socket_path);
|
||||||
|
if (bind(ipc_sock_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||||
|
close(ipc_sock_fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
listen(ipc_sock_fd, 16);
|
||||||
|
|
||||||
|
setenv("MANGO_INSTANCE_SIGNATURE", ipc_socket_path, 1);
|
||||||
|
|
||||||
|
ipc_event_source =
|
||||||
|
wl_event_loop_add_fd(event_loop, ipc_sock_fd, WL_EVENT_READABLE,
|
||||||
|
ipc_handle_connection, event_loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ipc_cleanup(void) {
|
||||||
|
if (ipc_event_source)
|
||||||
|
wl_event_source_remove(ipc_event_source);
|
||||||
|
if (ipc_sock_fd >= 0)
|
||||||
|
close(ipc_sock_fd);
|
||||||
|
unlink(ipc_socket_path);
|
||||||
|
unsetenv("MANGO_INSTANCE_SIGNATURE");
|
||||||
|
|
||||||
|
struct ipc_watch_client *wc, *tmp;
|
||||||
|
wl_list_for_each_safe(wc, tmp, &watch_clients, link)
|
||||||
|
ipc_remove_watch_client(wc);
|
||||||
|
}
|
||||||
62
src/mango.c
62
src/mango.c
|
|
@ -194,28 +194,6 @@ enum seat_config_shortcuts_inhibit {
|
||||||
SHORTCUTS_INHIBIT_ENABLE,
|
SHORTCUTS_INHIBIT_ENABLE,
|
||||||
};
|
};
|
||||||
|
|
||||||
// 事件掩码枚举
|
|
||||||
enum print_event_type {
|
|
||||||
PRINT_ACTIVE = 1 << 0,
|
|
||||||
PRINT_TAG = 1 << 1,
|
|
||||||
PRINT_LAYOUT = 1 << 2,
|
|
||||||
PRINT_TITLE = 1 << 3,
|
|
||||||
PRINT_APPID = 1 << 4,
|
|
||||||
PRINT_LAYOUT_SYMBOL = 1 << 5,
|
|
||||||
PRINT_FULLSCREEN = 1 << 6,
|
|
||||||
PRINT_FLOATING = 1 << 7,
|
|
||||||
PRINT_X = 1 << 8,
|
|
||||||
PRINT_Y = 1 << 9,
|
|
||||||
PRINT_WIDTH = 1 << 10,
|
|
||||||
PRINT_HEIGHT = 1 << 11,
|
|
||||||
PRINT_LAST_LAYER = 1 << 12,
|
|
||||||
PRINT_KB_LAYOUT = 1 << 13,
|
|
||||||
PRINT_KEYMODE = 1 << 14,
|
|
||||||
PRINT_SCALEFACTOR = 1 << 15,
|
|
||||||
PRINT_FRAME = 1 << 16,
|
|
||||||
PRINT_ALL = (1 << 17) - 1 // 所有位都设为1
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct Pertag Pertag;
|
typedef struct Pertag Pertag;
|
||||||
typedef struct Monitor Monitor;
|
typedef struct Monitor Monitor;
|
||||||
typedef struct Client Client;
|
typedef struct Client Client;
|
||||||
|
|
@ -238,6 +216,7 @@ typedef struct {
|
||||||
char *v3;
|
char *v3;
|
||||||
uint32_t ui;
|
uint32_t ui;
|
||||||
uint32_t ui2;
|
uint32_t ui2;
|
||||||
|
Client *tc;
|
||||||
} Arg;
|
} Arg;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -442,6 +421,7 @@ struct Client {
|
||||||
float old_grid_row_per;
|
float old_grid_row_per;
|
||||||
int32_t grid_col_idx;
|
int32_t grid_col_idx;
|
||||||
int32_t grid_row_idx;
|
int32_t grid_row_idx;
|
||||||
|
uint32_t id;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
@ -554,7 +534,7 @@ struct Monitor {
|
||||||
uint32_t visible_tiling_clients;
|
uint32_t visible_tiling_clients;
|
||||||
uint32_t visible_scroll_tiling_clients;
|
uint32_t visible_scroll_tiling_clients;
|
||||||
uint32_t visible_fake_tiling_clients;
|
uint32_t visible_fake_tiling_clients;
|
||||||
char last_surface_ws_name[256];
|
char last_open_surface[256];
|
||||||
struct wlr_ext_workspace_group_handle_v1 *ext_group;
|
struct wlr_ext_workspace_group_handle_v1 *ext_group;
|
||||||
bool iscleanuping;
|
bool iscleanuping;
|
||||||
int8_t carousel_anim_dir;
|
int8_t carousel_anim_dir;
|
||||||
|
|
@ -867,7 +847,8 @@ static Client *find_client_by_direction(Client *tc, const Arg *arg,
|
||||||
static void exit_scroller_stack(Client *c);
|
static void exit_scroller_stack(Client *c);
|
||||||
static Client *scroll_get_stack_head_client(Client *c);
|
static Client *scroll_get_stack_head_client(Client *c);
|
||||||
static bool client_only_in_one_tag(Client *c);
|
static bool client_only_in_one_tag(Client *c);
|
||||||
static Client *get_focused_stack_client(Client *sc);
|
static Client *get_focused_stack_client(Client *sc,
|
||||||
|
Client *custom_focus_client);
|
||||||
static bool client_is_in_same_stack(Client *sc, Client *tc, Client *fc);
|
static bool client_is_in_same_stack(Client *sc, Client *tc, Client *fc);
|
||||||
static void monitor_stop_skip_frame_timer(Monitor *m);
|
static void monitor_stop_skip_frame_timer(Monitor *m);
|
||||||
static int monitor_skip_frame_timeout_callback(void *data);
|
static int monitor_skip_frame_timeout_callback(void *data);
|
||||||
|
|
@ -1024,6 +1005,7 @@ static char *env_vars[] = {"DISPLAY",
|
||||||
"XDG_SESSION_TYPE",
|
"XDG_SESSION_TYPE",
|
||||||
"XCURSOR_THEME",
|
"XCURSOR_THEME",
|
||||||
"XCURSOR_SIZE",
|
"XCURSOR_SIZE",
|
||||||
|
"MANGO_INSTANCE_SIGNATURE",
|
||||||
NULL};
|
NULL};
|
||||||
static struct {
|
static struct {
|
||||||
enum wp_cursor_shape_device_v1_shape shape;
|
enum wp_cursor_shape_device_v1_shape shape;
|
||||||
|
|
@ -1115,6 +1097,7 @@ static struct wl_event_source *sync_keymap;
|
||||||
#include "dispatch/bind_define.h"
|
#include "dispatch/bind_define.h"
|
||||||
#include "ext-protocol/all.h"
|
#include "ext-protocol/all.h"
|
||||||
#include "fetch/fetch.h"
|
#include "fetch/fetch.h"
|
||||||
|
#include "ipc/ipc.h"
|
||||||
#include "layout/arrange.h"
|
#include "layout/arrange.h"
|
||||||
#include "layout/dwindle.h"
|
#include "layout/dwindle.h"
|
||||||
#include "layout/horizontal.h"
|
#include "layout/horizontal.h"
|
||||||
|
|
@ -2531,6 +2514,7 @@ void cleanuplisteners(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void cleanup(void) {
|
void cleanup(void) {
|
||||||
|
ipc_cleanup();
|
||||||
cleanuplisteners();
|
cleanuplisteners();
|
||||||
#ifdef XWAYLAND
|
#ifdef XWAYLAND
|
||||||
wlr_xwayland_destroy(xwayland);
|
wlr_xwayland_destroy(xwayland);
|
||||||
|
|
@ -2656,9 +2640,9 @@ void maplayersurfacenotify(struct wl_listener *listener, void *data) {
|
||||||
|
|
||||||
if (!l->mon)
|
if (!l->mon)
|
||||||
return;
|
return;
|
||||||
strncpy(l->mon->last_surface_ws_name, layer_surface->namespace,
|
strncpy(l->mon->last_open_surface, layer_surface->namespace,
|
||||||
sizeof(l->mon->last_surface_ws_name) - 1); // 最多拷贝255个字符
|
sizeof(l->mon->last_open_surface) - 1); // 最多拷贝255个字符
|
||||||
l->mon->last_surface_ws_name[sizeof(l->mon->last_surface_ws_name) - 1] =
|
l->mon->last_open_surface[sizeof(l->mon->last_open_surface) - 1] =
|
||||||
'\0'; // 确保字符串以null结尾
|
'\0'; // 确保字符串以null结尾
|
||||||
|
|
||||||
// 初始化几何位置
|
// 初始化几何位置
|
||||||
|
|
@ -4341,6 +4325,8 @@ mapnotify(struct wl_listener *listener, void *data) {
|
||||||
Client *c = wl_container_of(listener, c, map);
|
Client *c = wl_container_of(listener, c, map);
|
||||||
int32_t i = 0;
|
int32_t i = 0;
|
||||||
|
|
||||||
|
c->id = generate_client_id();
|
||||||
|
|
||||||
/* Create scene tree for this client and its border */
|
/* Create scene tree for this client and its border */
|
||||||
c->scene = client_surface(c)->data = wlr_scene_tree_create(layers[LyrTile]);
|
c->scene = client_surface(c)->data = wlr_scene_tree_create(layers[LyrTile]);
|
||||||
wlr_scene_node_set_enabled(&c->scene->node, c->type != XDGShell);
|
wlr_scene_node_set_enabled(&c->scene->node, c->type != XDGShell);
|
||||||
|
|
@ -5722,13 +5708,30 @@ void create_output(struct wlr_backend *backend, void *data) {
|
||||||
// 修改信号处理函数,接收掩码参数
|
// 修改信号处理函数,接收掩码参数
|
||||||
void handle_print_status(struct wl_listener *listener, void *data) {
|
void handle_print_status(struct wl_listener *listener, void *data) {
|
||||||
|
|
||||||
|
ipc_notify_keymode();
|
||||||
|
ipc_notify_kb_layout();
|
||||||
|
ipc_notify_all_tags();
|
||||||
|
ipc_notify_all_clients();
|
||||||
|
ipc_notify_all_monitors();
|
||||||
|
|
||||||
|
Client *c = NULL;
|
||||||
|
wl_list_for_each(c, &clients, link) {
|
||||||
|
if (c->iskilling)
|
||||||
|
continue;
|
||||||
|
ipc_notify_client(c);
|
||||||
|
}
|
||||||
|
|
||||||
Monitor *m = NULL;
|
Monitor *m = NULL;
|
||||||
wl_list_for_each(m, &mons, link) {
|
wl_list_for_each(m, &mons, link) {
|
||||||
if (!m->wlr_output->enabled) {
|
if (!m->wlr_output->enabled) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
dwl_ext_workspace_printstatus(m);
|
|
||||||
|
|
||||||
|
ipc_notify_monitor(m);
|
||||||
|
ipc_notify_tags(m);
|
||||||
|
ipc_notify_last_surface_ws_name(m);
|
||||||
|
|
||||||
|
dwl_ext_workspace_printstatus(m);
|
||||||
dwl_ipc_output_printstatus(m);
|
dwl_ipc_output_printstatus(m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5757,6 +5760,9 @@ void setup(void) {
|
||||||
* clients from the Unix socket, manging Wayland globals, and so on. */
|
* clients from the Unix socket, manging Wayland globals, and so on. */
|
||||||
dpy = wl_display_create();
|
dpy = wl_display_create();
|
||||||
event_loop = wl_display_get_event_loop(dpy);
|
event_loop = wl_display_get_event_loop(dpy);
|
||||||
|
|
||||||
|
ipc_init(event_loop);
|
||||||
|
|
||||||
tablet_mgr = wlr_tablet_v2_create(dpy);
|
tablet_mgr = wlr_tablet_v2_create(dpy);
|
||||||
/* The backend is a wlroots feature which abstracts the underlying input
|
/* The backend is a wlroots feature which abstracts the underlying input
|
||||||
* and output hardware. The autocreate option will choose the most
|
* and output hardware. The autocreate option will choose the most
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue