mirror of
https://github.com/labwc/labwc.git
synced 2026-06-13 14:33:18 -04:00
Merge 0985a44982 into f4b9bdab65
This commit is contained in:
commit
81666b458d
25 changed files with 2674 additions and 3 deletions
391
clients/labmsg.c
Normal file
391
clients/labmsg.c
Normal file
|
|
@ -0,0 +1,391 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* labmsg - IPC client for labwc (swaymsg-compatible interface)
|
||||||
|
*/
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <json-c/json.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define IPC_MAGIC "labwc-ipc"
|
||||||
|
#define IPC_MAGIC_LEN 9
|
||||||
|
#define IPC_HEADER_SIZE (IPC_MAGIC_LEN + 4 + 4)
|
||||||
|
|
||||||
|
/* Message types */
|
||||||
|
enum ipc_msg_type {
|
||||||
|
IPC_RUN_COMMAND = 0,
|
||||||
|
IPC_GET_WORKSPACES = 1,
|
||||||
|
IPC_SUBSCRIBE = 2,
|
||||||
|
IPC_GET_OUTPUTS = 3,
|
||||||
|
IPC_GET_TREE = 4,
|
||||||
|
IPC_GET_BAR_CONFIG = 6,
|
||||||
|
IPC_GET_VERSION = 7,
|
||||||
|
IPC_GET_CONFIG = 9,
|
||||||
|
IPC_SEND_TICK = 10,
|
||||||
|
IPC_SYNC = 11,
|
||||||
|
IPC_GET_INPUTS = 100,
|
||||||
|
IPC_GET_SEATS = 101,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct option long_options[] = {{"help", no_argument, NULL, 'h'},
|
||||||
|
{"monitor", no_argument, NULL, 'm'}, {"pretty", no_argument, NULL, 'p'},
|
||||||
|
{"quiet", no_argument, NULL, 'q'}, {"raw", no_argument, NULL, 'r'},
|
||||||
|
{"socket", required_argument, NULL, 's'},
|
||||||
|
{"type", required_argument, NULL, 't'},
|
||||||
|
{"version", no_argument, NULL, 'v'}, {0, 0, 0, 0}};
|
||||||
|
|
||||||
|
static const char usage_str[] =
|
||||||
|
"Usage: labmsg [options] [message]\n"
|
||||||
|
"\n"
|
||||||
|
"Options:\n"
|
||||||
|
" -h, --help Show help and exit\n"
|
||||||
|
" -m, --monitor Monitor for events (subscribe mode)\n"
|
||||||
|
" -p, --pretty Force pretty-printed JSON output\n"
|
||||||
|
" -q, --quiet Suppress response output\n"
|
||||||
|
" -r, --raw Force raw JSON output\n"
|
||||||
|
" -s, --socket <path> Override socket path\n"
|
||||||
|
" -t, --type <type> Message type (default: run_command)\n"
|
||||||
|
" -v, --version Show version and exit\n"
|
||||||
|
"\n"
|
||||||
|
"Message types:\n"
|
||||||
|
" run_command, get_workspaces, subscribe, get_outputs,\n"
|
||||||
|
" get_tree, get_bar_config, get_version, get_config,\n"
|
||||||
|
" send_tick, get_inputs, get_seats\n";
|
||||||
|
|
||||||
|
static int
|
||||||
|
parse_msg_type(const char *name)
|
||||||
|
{
|
||||||
|
if (!name || !strcmp(name, "run_command")) {
|
||||||
|
return IPC_RUN_COMMAND;
|
||||||
|
} else if (!strcmp(name, "get_workspaces")) {
|
||||||
|
return IPC_GET_WORKSPACES;
|
||||||
|
} else if (!strcmp(name, "subscribe")) {
|
||||||
|
return IPC_SUBSCRIBE;
|
||||||
|
} else if (!strcmp(name, "get_outputs")) {
|
||||||
|
return IPC_GET_OUTPUTS;
|
||||||
|
} else if (!strcmp(name, "get_tree")) {
|
||||||
|
return IPC_GET_TREE;
|
||||||
|
} else if (!strcmp(name, "get_bar_config")) {
|
||||||
|
return IPC_GET_BAR_CONFIG;
|
||||||
|
} else if (!strcmp(name, "get_version")) {
|
||||||
|
return IPC_GET_VERSION;
|
||||||
|
} else if (!strcmp(name, "get_config")) {
|
||||||
|
return IPC_GET_CONFIG;
|
||||||
|
} else if (!strcmp(name, "send_tick")) {
|
||||||
|
return IPC_SEND_TICK;
|
||||||
|
} else if (!strcmp(name, "get_inputs")) {
|
||||||
|
return IPC_GET_INPUTS;
|
||||||
|
} else if (!strcmp(name, "get_seats")) {
|
||||||
|
return IPC_GET_SEATS;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "Unknown message type: %s\n", name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ipc_connect(const char *socket_path)
|
||||||
|
{
|
||||||
|
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
|
if (fd < 0) {
|
||||||
|
perror("socket");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_un addr = {0};
|
||||||
|
addr.sun_family = AF_UNIX;
|
||||||
|
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
|
||||||
|
|
||||||
|
if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||||
|
perror("connect");
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
ipc_send(int fd, uint32_t type, const char *payload, uint32_t len)
|
||||||
|
{
|
||||||
|
char header[IPC_HEADER_SIZE];
|
||||||
|
memcpy(header, IPC_MAGIC, IPC_MAGIC_LEN);
|
||||||
|
memcpy(header + IPC_MAGIC_LEN, &len, sizeof(uint32_t));
|
||||||
|
memcpy(header + IPC_MAGIC_LEN + 4, &type, sizeof(uint32_t));
|
||||||
|
|
||||||
|
if (write(fd, header, IPC_HEADER_SIZE) != IPC_HEADER_SIZE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (len > 0 && write(fd, payload, len) != (ssize_t)len) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
read_exact(int fd, void *buf, size_t count)
|
||||||
|
{
|
||||||
|
size_t total = 0;
|
||||||
|
while (total < count) {
|
||||||
|
ssize_t n = read(fd, (char *)buf + total, count - total);
|
||||||
|
if (n <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
total += n;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
ipc_recv(int fd, uint32_t *type, char **payload, uint32_t *len)
|
||||||
|
{
|
||||||
|
char header[IPC_HEADER_SIZE];
|
||||||
|
if (!read_exact(fd, header, IPC_HEADER_SIZE)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memcmp(header, IPC_MAGIC, IPC_MAGIC_LEN) != 0) {
|
||||||
|
fprintf(stderr, "Invalid IPC response magic\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(len, header + IPC_MAGIC_LEN, sizeof(uint32_t));
|
||||||
|
memcpy(type, header + IPC_MAGIC_LEN + 4, sizeof(uint32_t));
|
||||||
|
|
||||||
|
if (*len > 0) {
|
||||||
|
*payload = malloc(*len + 1);
|
||||||
|
if (!*payload) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!read_exact(fd, *payload, *len)) {
|
||||||
|
free(*payload);
|
||||||
|
*payload = NULL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
(*payload)[*len] = '\0';
|
||||||
|
} else {
|
||||||
|
*payload = NULL;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
print_json(const char *data, bool pretty)
|
||||||
|
{
|
||||||
|
struct json_object *obj = json_tokener_parse(data);
|
||||||
|
if (!obj) {
|
||||||
|
/* Not valid JSON, print as-is */
|
||||||
|
printf("%s\n", data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int flags = JSON_C_TO_STRING_NOSLASHESCAPE;
|
||||||
|
if (pretty) {
|
||||||
|
flags |= JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED;
|
||||||
|
} else {
|
||||||
|
flags |= JSON_C_TO_STRING_PLAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s\n", json_object_to_json_string_ext(obj, flags));
|
||||||
|
json_object_put(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if any command in the result array failed */
|
||||||
|
static bool
|
||||||
|
check_command_success(const char *data)
|
||||||
|
{
|
||||||
|
struct json_object *obj = json_tokener_parse(data);
|
||||||
|
if (!obj) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = true;
|
||||||
|
if (json_object_get_type(obj) == json_type_array) {
|
||||||
|
int len = json_object_array_length(obj);
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
struct json_object *item =
|
||||||
|
json_object_array_get_idx(obj, i);
|
||||||
|
struct json_object *s = NULL;
|
||||||
|
if (json_object_object_get_ex(item, "success", &s)) {
|
||||||
|
if (!json_object_get_boolean(s)) {
|
||||||
|
success = false;
|
||||||
|
/* Print error to stderr */
|
||||||
|
struct json_object *err = NULL;
|
||||||
|
if (json_object_object_get_ex(item,
|
||||||
|
"error", &err)) {
|
||||||
|
fprintf(stderr, "Error: %s\n",
|
||||||
|
json_object_get_string(
|
||||||
|
err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (json_object_get_type(obj) == json_type_object) {
|
||||||
|
struct json_object *s = NULL;
|
||||||
|
if (json_object_object_get_ex(obj, "success", &s)) {
|
||||||
|
success = json_object_get_boolean(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object_put(obj);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
setvbuf(stdout, NULL, _IOLBF, 0);
|
||||||
|
|
||||||
|
bool monitor = false;
|
||||||
|
bool pretty = false;
|
||||||
|
bool raw = false;
|
||||||
|
bool quiet = false;
|
||||||
|
const char *socket_path = NULL;
|
||||||
|
const char *type_str = NULL;
|
||||||
|
bool force_pretty = false;
|
||||||
|
|
||||||
|
int c;
|
||||||
|
while (1) {
|
||||||
|
int index = 0;
|
||||||
|
c = getopt_long(argc, argv, "hmpqrs:t:v", long_options, &index);
|
||||||
|
if (c == -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (c) {
|
||||||
|
case 'h':
|
||||||
|
printf("%s", usage_str);
|
||||||
|
return 0;
|
||||||
|
case 'm':
|
||||||
|
monitor = true;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
pretty = true;
|
||||||
|
force_pretty = true;
|
||||||
|
break;
|
||||||
|
case 'q':
|
||||||
|
quiet = true;
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
raw = true;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
socket_path = optarg;
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
type_str = optarg;
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
printf("labmsg " LABWC_VERSION "\n");
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "%s", usage_str);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Auto-detect pretty mode */
|
||||||
|
if (!force_pretty && !raw) {
|
||||||
|
pretty = isatty(STDOUT_FILENO);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Determine message type */
|
||||||
|
int msg_type = parse_msg_type(type_str);
|
||||||
|
if (msg_type < 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Build payload from remaining args */
|
||||||
|
char *payload = NULL;
|
||||||
|
if (optind < argc) {
|
||||||
|
/* Concatenate remaining args with spaces */
|
||||||
|
size_t total = 0;
|
||||||
|
for (int i = optind; i < argc; i++) {
|
||||||
|
total += strlen(argv[i]) + 1;
|
||||||
|
}
|
||||||
|
payload = malloc(total);
|
||||||
|
payload[0] = '\0';
|
||||||
|
for (int i = optind; i < argc; i++) {
|
||||||
|
if (i > optind) {
|
||||||
|
strcat(payload, " ");
|
||||||
|
}
|
||||||
|
strcat(payload, argv[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get socket path */
|
||||||
|
if (!socket_path) {
|
||||||
|
socket_path = getenv("LABWC_IPC_SOCK");
|
||||||
|
}
|
||||||
|
if (!socket_path) {
|
||||||
|
fprintf(stderr, "LABWC_IPC_SOCK not set and no -s option\n");
|
||||||
|
free(payload);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fd = ipc_connect(socket_path);
|
||||||
|
if (fd < 0) {
|
||||||
|
fprintf(stderr, "Failed to connect to %s\n", socket_path);
|
||||||
|
free(payload);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send message */
|
||||||
|
uint32_t payload_len = payload ? strlen(payload) : 0;
|
||||||
|
if (!ipc_send(fd, msg_type, payload, payload_len)) {
|
||||||
|
fprintf(stderr, "Failed to send IPC message\n");
|
||||||
|
close(fd);
|
||||||
|
free(payload);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
free(payload);
|
||||||
|
|
||||||
|
/* Receive response */
|
||||||
|
uint32_t resp_type = 0;
|
||||||
|
char *resp_payload = NULL;
|
||||||
|
uint32_t resp_len = 0;
|
||||||
|
|
||||||
|
if (!ipc_recv(fd, &resp_type, &resp_payload, &resp_len)) {
|
||||||
|
fprintf(stderr, "Failed to receive IPC response\n");
|
||||||
|
close(fd);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int exit_code = 0;
|
||||||
|
|
||||||
|
if (!quiet && resp_payload) {
|
||||||
|
print_json(resp_payload, pretty);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for server-reported errors */
|
||||||
|
if (resp_payload && msg_type == IPC_RUN_COMMAND) {
|
||||||
|
if (!check_command_success(resp_payload)) {
|
||||||
|
exit_code = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Monitor mode: keep reading events */
|
||||||
|
if (monitor) {
|
||||||
|
free(resp_payload);
|
||||||
|
while (1) {
|
||||||
|
resp_payload = NULL;
|
||||||
|
if (!ipc_recv(fd, &resp_type, &resp_payload,
|
||||||
|
&resp_len)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!quiet && resp_payload) {
|
||||||
|
print_json(resp_payload, pretty);
|
||||||
|
}
|
||||||
|
free(resp_payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(resp_payload);
|
||||||
|
close(fd);
|
||||||
|
return exit_code;
|
||||||
|
}
|
||||||
|
|
@ -59,3 +59,11 @@ endif
|
||||||
|
|
||||||
clients = files('lab-sensible-terminal')
|
clients = files('lab-sensible-terminal')
|
||||||
install_data(clients, install_dir: get_option('bindir'))
|
install_data(clients, install_dir: get_option('bindir'))
|
||||||
|
|
||||||
|
jsonc_client = dependency('json-c')
|
||||||
|
executable(
|
||||||
|
'labmsg',
|
||||||
|
files('labmsg.c'),
|
||||||
|
dependencies: [jsonc_client],
|
||||||
|
install: true,
|
||||||
|
)
|
||||||
|
|
|
||||||
287
docs/labmsg.1.scd
Normal file
287
docs/labmsg.1.scd
Normal file
|
|
@ -0,0 +1,287 @@
|
||||||
|
labmsg(1)
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
labmsg - IPC client for labwc
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
|
||||||
|
_labmsg_ [options...] [message]
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
labmsg is a command-line IPC client for *labwc*(1). It communicates with a
|
||||||
|
running labwc instance over a UNIX domain socket using a swaymsg-compatible
|
||||||
|
binary protocol.
|
||||||
|
|
||||||
|
The socket path is obtained from the *LABWC_IPC_SOCK* environment variable,
|
||||||
|
which labwc sets automatically for child processes. It can also be specified
|
||||||
|
explicitly with the *-s* option.
|
||||||
|
|
||||||
|
# OPTIONS
|
||||||
|
|
||||||
|
*-h, --help*
|
||||||
|
Show help message and quit.
|
||||||
|
|
||||||
|
*-m, --monitor*
|
||||||
|
After sending the initial message, remain connected and continuously
|
||||||
|
print events received from the compositor. Typically used with
|
||||||
|
*-t subscribe*.
|
||||||
|
|
||||||
|
*-p, --pretty*
|
||||||
|
Force pretty-printed (indented) JSON output.
|
||||||
|
|
||||||
|
*-q, --quiet*
|
||||||
|
Suppress response output. The exit code still reflects success or
|
||||||
|
failure.
|
||||||
|
|
||||||
|
*-r, --raw*
|
||||||
|
Force compact (single-line) JSON output. When neither *-p* nor *-r* is
|
||||||
|
given, labmsg auto-detects: pretty if stdout is a terminal, raw
|
||||||
|
otherwise.
|
||||||
|
|
||||||
|
*-s, --socket* <path>
|
||||||
|
Use _path_ as the IPC socket instead of *LABWC_IPC_SOCK*.
|
||||||
|
|
||||||
|
*-t, --type* <type>
|
||||||
|
Set the IPC message type. The default is *run_command*. See *MESSAGE
|
||||||
|
TYPES* below for the full list.
|
||||||
|
|
||||||
|
*-v, --version*
|
||||||
|
Show the version number and quit.
|
||||||
|
|
||||||
|
# MESSAGE TYPES
|
||||||
|
|
||||||
|
The following message types are supported via *-t*:
|
||||||
|
|
||||||
|
*run_command*
|
||||||
|
Execute one or more compositor commands (the default). Commands are
|
||||||
|
passed as the trailing message argument. Multiple commands can be
|
||||||
|
separated by semicolons. See *COMMANDS* below.
|
||||||
|
|
||||||
|
*get_workspaces*
|
||||||
|
Return a JSON array of workspace objects, each containing _num_, _name_,
|
||||||
|
_visible_, _focused_, _urgent_, _output_, and _rect_ fields.
|
||||||
|
|
||||||
|
*subscribe*
|
||||||
|
Subscribe to compositor events. The message payload must be a JSON array
|
||||||
|
of event type names. See *EVENTS* below. Typically combined with *-m*.
|
||||||
|
|
||||||
|
*get_outputs*
|
||||||
|
Return a JSON array of output objects with properties such as _name_,
|
||||||
|
_make_, _model_, _serial_, _active_, _scale_, _transform_,
|
||||||
|
_current_workspace_, _modes_, _current_mode_, and _rect_.
|
||||||
|
|
||||||
|
*get_tree*
|
||||||
|
Return the full window tree as a nested JSON object. The root contains
|
||||||
|
output nodes, each containing workspace nodes, each containing
|
||||||
|
floating view nodes.
|
||||||
|
|
||||||
|
*get_bar_config*
|
||||||
|
Return bar configuration. Since labwc has no built-in bar, this returns
|
||||||
|
an empty array (or empty object if a bar ID is given).
|
||||||
|
|
||||||
|
*get_version*
|
||||||
|
Return a JSON object with _major_, _minor_, _patch_,
|
||||||
|
_human_readable_, and _loaded_config_file_name_ fields.
|
||||||
|
|
||||||
|
*get_config*
|
||||||
|
Return a JSON object with a _config_ field containing the raw text of
|
||||||
|
the currently loaded rc.xml configuration file.
|
||||||
|
|
||||||
|
*send_tick*
|
||||||
|
Broadcast a tick event to all subscribers. The message payload is
|
||||||
|
forwarded as the tick _payload_ string.
|
||||||
|
|
||||||
|
*get_inputs*
|
||||||
|
Return a JSON array of input device objects with _identifier_, _name_,
|
||||||
|
_vendor_, _product_, _type_, and device-specific fields such as
|
||||||
|
_xkb_active_layout_name_ for keyboards or _scroll_factor_ for
|
||||||
|
pointers.
|
||||||
|
|
||||||
|
*get_seats*
|
||||||
|
Return a JSON array of seat objects with _name_, _capabilities_,
|
||||||
|
_focus_, and a _devices_ array.
|
||||||
|
|
||||||
|
# COMMANDS
|
||||||
|
|
||||||
|
When the message type is *run_command* (the default), the trailing arguments
|
||||||
|
form a command string. Multiple commands can be separated by semicolons (*;*).
|
||||||
|
|
||||||
|
Commands may be prefixed with a criteria block to target specific windows:
|
||||||
|
|
||||||
|
\[app_id="<pattern>" title="<pattern>" class="<pattern>"\]
|
||||||
|
|
||||||
|
If no criteria are given, the command operates on the currently focused window
|
||||||
|
where applicable.
|
||||||
|
|
||||||
|
The following commands are supported:
|
||||||
|
|
||||||
|
*nop*
|
||||||
|
No operation.
|
||||||
|
|
||||||
|
*exit*
|
||||||
|
Exit the compositor.
|
||||||
|
|
||||||
|
*reload*
|
||||||
|
Reload the compositor configuration.
|
||||||
|
|
||||||
|
*exec* [--no-startup-id] _<command>_
|
||||||
|
Execute _command_ via the shell.
|
||||||
|
|
||||||
|
*kill*
|
||||||
|
Close the target window.
|
||||||
|
|
||||||
|
*fullscreen* [toggle]
|
||||||
|
Toggle fullscreen mode on the target window.
|
||||||
|
|
||||||
|
*floating toggle*
|
||||||
|
If the target window is tiled, untile it. Otherwise no-op (labwc is a
|
||||||
|
stacking compositor).
|
||||||
|
|
||||||
|
*sticky toggle*
|
||||||
|
Toggle the target window's omnipresent (visible on all workspaces) state.
|
||||||
|
|
||||||
|
*border* none|normal|pixel|toggle
|
||||||
|
Set or toggle the target window's border decoration mode.
|
||||||
|
|
||||||
|
*workspace* _<name>_
|
||||||
|
Switch to the named workspace. The special names *next*, *prev*,
|
||||||
|
*next_on_output*, and *prev_on_output* cycle through workspaces.
|
||||||
|
|
||||||
|
*focus output* _<name>_|left|right|up|down
|
||||||
|
Focus the given output by name or direction.
|
||||||
|
|
||||||
|
*focus*
|
||||||
|
Focus the target window. Raises the window to the front and gives it
|
||||||
|
keyboard focus. Typically used with criteria to focus a specific
|
||||||
|
window, e.g. *labmsg '[app_id="firefox"] focus'*.
|
||||||
|
|
||||||
|
*focus next*
|
||||||
|
Focus the next window in z-order below the current one. Wraps to the
|
||||||
|
topmost window if the current window is at the bottom.
|
||||||
|
|
||||||
|
*focus prev* (or *focus last*)
|
||||||
|
Focus the previous window in z-order above the current one. Wraps to
|
||||||
|
the bottommost window if the current window is at the top.
|
||||||
|
|
||||||
|
*move position* _<x>_ _<y>_
|
||||||
|
Move the target window to absolute coordinates.
|
||||||
|
|
||||||
|
*move* [container|window] [to] workspace _<name>_
|
||||||
|
Move the target window to the named workspace.
|
||||||
|
|
||||||
|
*move* [container|window] [to] output _<name>_|left|right|up|down
|
||||||
|
Move the target window to the given output.
|
||||||
|
|
||||||
|
*move* left|right|up|down [_<pixels>_]
|
||||||
|
Move the target window by _pixels_ in the given direction (default 10).
|
||||||
|
|
||||||
|
*resize set* _<width>_ _<height>_
|
||||||
|
Resize the target window to exact dimensions.
|
||||||
|
|
||||||
|
*resize* grow|shrink width|height _<amount>_ [px|ppt]
|
||||||
|
Resize the target window incrementally (default amount 10).
|
||||||
|
|
||||||
|
# EVENTS
|
||||||
|
|
||||||
|
When using *-t subscribe*, the message payload must be a JSON array of event
|
||||||
|
type names. For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
labmsg -t subscribe -m '["workspace", "window"]'
|
||||||
|
```
|
||||||
|
|
||||||
|
The following event types are available:
|
||||||
|
|
||||||
|
*workspace*
|
||||||
|
Emitted on workspace changes. The event object contains _change_
|
||||||
|
(*"focus"*, *"init"*, etc.), _current_, and _old_ fields.
|
||||||
|
|
||||||
|
*output*
|
||||||
|
Emitted on output changes. Contains a _change_ field.
|
||||||
|
|
||||||
|
*window*
|
||||||
|
Emitted on window changes. Contains _change_ (*"new"*, *"close"*,
|
||||||
|
*"focus"*, *"title"*, *"fullscreen_mode"*, *"move"*, *"resize"*) and a
|
||||||
|
_container_ field with the view properties.
|
||||||
|
|
||||||
|
*shutdown*
|
||||||
|
Emitted when the compositor is about to exit. Contains _change_
|
||||||
|
(*"exit"*).
|
||||||
|
|
||||||
|
*tick*
|
||||||
|
Emitted when a *send_tick* message is received. Contains _first_ (boolean,
|
||||||
|
true for the initial tick on subscription) and _payload_ fields.
|
||||||
|
|
||||||
|
# ENVIRONMENT
|
||||||
|
|
||||||
|
*LABWC_IPC_SOCK*
|
||||||
|
Path to the labwc IPC UNIX domain socket. Set automatically by labwc
|
||||||
|
for child processes. Overridden by *-s*.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
Query the current workspaces:
|
||||||
|
|
||||||
|
```
|
||||||
|
labmsg -t get_workspaces
|
||||||
|
```
|
||||||
|
|
||||||
|
Get the window tree in compact form:
|
||||||
|
|
||||||
|
```
|
||||||
|
labmsg -r -t get_tree
|
||||||
|
```
|
||||||
|
|
||||||
|
Close the focused window:
|
||||||
|
|
||||||
|
```
|
||||||
|
labmsg kill
|
||||||
|
```
|
||||||
|
|
||||||
|
Move a specific window to workspace 2:
|
||||||
|
|
||||||
|
```
|
||||||
|
labmsg '[app_id="firefox"] move container to workspace 2'
|
||||||
|
```
|
||||||
|
|
||||||
|
Subscribe to window events:
|
||||||
|
|
||||||
|
```
|
||||||
|
labmsg -t subscribe -m '["window"]'
|
||||||
|
```
|
||||||
|
|
||||||
|
Execute a command:
|
||||||
|
|
||||||
|
```
|
||||||
|
labmsg exec foot
|
||||||
|
```
|
||||||
|
|
||||||
|
Chain multiple commands:
|
||||||
|
|
||||||
|
```
|
||||||
|
labmsg 'workspace 3; exec firefox'
|
||||||
|
```
|
||||||
|
|
||||||
|
Focus a specific window by application ID:
|
||||||
|
|
||||||
|
```
|
||||||
|
labmsg '[app_id="foot"] focus'
|
||||||
|
```
|
||||||
|
|
||||||
|
Cycle focus to the next window:
|
||||||
|
|
||||||
|
```
|
||||||
|
labmsg 'focus next'
|
||||||
|
```
|
||||||
|
|
||||||
|
Cycle focus to the previous window:
|
||||||
|
|
||||||
|
```
|
||||||
|
labmsg 'focus prev'
|
||||||
|
```
|
||||||
|
|
||||||
|
# SEE ALSO
|
||||||
|
|
||||||
|
labwc(1), labwc-actions(5)
|
||||||
|
|
@ -170,6 +170,13 @@ example: *LABWC_DEBUG_FOO=1 labwc*.
|
||||||
Enable logging of press and release events for bound keys (generally
|
Enable logging of press and release events for bound keys (generally
|
||||||
key-combinations like *Ctrl-Alt-t*).
|
key-combinations like *Ctrl-Alt-t*).
|
||||||
|
|
||||||
|
*LABWC_IPC_SOCK*
|
||||||
|
Path to the IPC UNIX domain socket for the running labwc instance.
|
||||||
|
Set automatically by labwc at startup to
|
||||||
|
_$XDG_RUNTIME_DIR/labwc-ipc.<pid>.sock_ and exported to child
|
||||||
|
processes. Used by *labmsg*(1) to connect to the compositor. Can be
|
||||||
|
overridden to connect to a specific instance.
|
||||||
|
|
||||||
# SEE ALSO
|
# SEE ALSO
|
||||||
|
|
||||||
labwc-actions(5), labwc-config(5), labwc-menu(5), labwc-theme(5)
|
labmsg(1), labwc-actions(5), labwc-config(5), labwc-menu(5), labwc-theme(5)
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ if scdoc.found()
|
||||||
'labwc-menu.5',
|
'labwc-menu.5',
|
||||||
'labwc-theme.5',
|
'labwc-theme.5',
|
||||||
'labnag.1',
|
'labnag.1',
|
||||||
|
'labmsg.1',
|
||||||
]
|
]
|
||||||
foreach manpage : manpages
|
foreach manpage : manpages
|
||||||
markdown = manpage + '.scd'
|
markdown = manpage + '.scd'
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ bool action_is_valid(struct action *action);
|
||||||
bool action_is_show_menu(struct action *action);
|
bool action_is_show_menu(struct action *action);
|
||||||
|
|
||||||
void action_arg_add_str(struct action *action, const char *key, const char *value);
|
void action_arg_add_str(struct action *action, const char *key, const char *value);
|
||||||
|
void action_arg_add_int(struct action *action, const char *key, int value);
|
||||||
void action_arg_add_actionlist(struct action *action, const char *key);
|
void action_arg_add_actionlist(struct action *action, const char *key);
|
||||||
void action_arg_add_querylist(struct action *action, const char *key);
|
void action_arg_add_querylist(struct action *action, const char *key);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@ struct rcxml {
|
||||||
char *config_dir;
|
char *config_dir;
|
||||||
char *config_file;
|
char *config_file;
|
||||||
bool merge_config;
|
bool merge_config;
|
||||||
|
char *loaded_config_file;
|
||||||
|
|
||||||
/* core */
|
/* core */
|
||||||
bool xdg_shell_server_side_deco;
|
bool xdg_shell_server_side_deco;
|
||||||
|
|
|
||||||
|
|
@ -9,5 +9,6 @@ struct foreign_toplevel *foreign_toplevel_create(struct view *view);
|
||||||
void foreign_toplevel_set_parent(struct foreign_toplevel *toplevel,
|
void foreign_toplevel_set_parent(struct foreign_toplevel *toplevel,
|
||||||
struct foreign_toplevel *parent);
|
struct foreign_toplevel *parent);
|
||||||
void foreign_toplevel_destroy(struct foreign_toplevel *toplevel);
|
void foreign_toplevel_destroy(struct foreign_toplevel *toplevel);
|
||||||
|
const char *foreign_toplevel_get_identifier(struct foreign_toplevel *toplevel);
|
||||||
|
|
||||||
#endif /* LABWC_FOREIGN_TOPLEVEL_H */
|
#endif /* LABWC_FOREIGN_TOPLEVEL_H */
|
||||||
|
|
|
||||||
77
include/ipc.h
Normal file
77
include/ipc.h
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||||
|
#ifndef LABWC_IPC_H
|
||||||
|
#define LABWC_IPC_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <wayland-util.h>
|
||||||
|
|
||||||
|
struct view;
|
||||||
|
struct workspace;
|
||||||
|
|
||||||
|
/* Wire protocol */
|
||||||
|
#define IPC_MAGIC "labwc-ipc"
|
||||||
|
#define IPC_MAGIC_LEN 9
|
||||||
|
#define IPC_HEADER_SIZE (IPC_MAGIC_LEN + 4 + 4) /* 17 bytes */
|
||||||
|
|
||||||
|
/* Message types (same numbering as sway/i3) */
|
||||||
|
enum ipc_msg_type {
|
||||||
|
IPC_RUN_COMMAND = 0,
|
||||||
|
IPC_GET_WORKSPACES = 1,
|
||||||
|
IPC_SUBSCRIBE = 2,
|
||||||
|
IPC_GET_OUTPUTS = 3,
|
||||||
|
IPC_GET_TREE = 4,
|
||||||
|
IPC_GET_MARKS = 5,
|
||||||
|
IPC_GET_BAR_CONFIG = 6,
|
||||||
|
IPC_GET_VERSION = 7,
|
||||||
|
IPC_GET_BINDING_MODES = 8,
|
||||||
|
IPC_GET_CONFIG = 9,
|
||||||
|
IPC_SEND_TICK = 10,
|
||||||
|
IPC_SYNC = 11,
|
||||||
|
IPC_GET_BINDING_STATE = 12,
|
||||||
|
IPC_GET_INPUTS = 100,
|
||||||
|
IPC_GET_SEATS = 101,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Event types (bit 31 set) */
|
||||||
|
#define IPC_EVENT_FLAG 0x80000000
|
||||||
|
enum ipc_event_type {
|
||||||
|
IPC_EVENT_WORKSPACE = (IPC_EVENT_FLAG | 0),
|
||||||
|
IPC_EVENT_OUTPUT = (IPC_EVENT_FLAG | 1),
|
||||||
|
IPC_EVENT_WINDOW = (IPC_EVENT_FLAG | 3),
|
||||||
|
IPC_EVENT_SHUTDOWN = (IPC_EVENT_FLAG | 6),
|
||||||
|
IPC_EVENT_TICK = (IPC_EVENT_FLAG | 7),
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Subscription bitmask */
|
||||||
|
#define IPC_SUB_WORKSPACE (1 << 0)
|
||||||
|
#define IPC_SUB_OUTPUT (1 << 1)
|
||||||
|
#define IPC_SUB_WINDOW (1 << 3)
|
||||||
|
#define IPC_SUB_SHUTDOWN (1 << 6)
|
||||||
|
#define IPC_SUB_TICK (1 << 7)
|
||||||
|
|
||||||
|
struct ipc_client {
|
||||||
|
struct wl_list link; /* server.ipc_clients */
|
||||||
|
int fd;
|
||||||
|
struct wl_event_source *readable;
|
||||||
|
struct wl_event_source *writable;
|
||||||
|
uint32_t subscriptions;
|
||||||
|
/* Write buffer for partial writes */
|
||||||
|
char *write_buf;
|
||||||
|
size_t write_buf_len;
|
||||||
|
size_t write_buf_cap;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Server lifecycle */
|
||||||
|
void ipc_init(void);
|
||||||
|
void ipc_finish(void);
|
||||||
|
|
||||||
|
/* Event emitters (called from compositor hooks) */
|
||||||
|
void ipc_event_workspace(const char *change, struct workspace *current,
|
||||||
|
struct workspace *old);
|
||||||
|
void ipc_event_output(const char *change);
|
||||||
|
void ipc_event_window(const char *change, struct view *view);
|
||||||
|
void ipc_event_window_geometry(struct view *view, struct wlr_box *new_geo);
|
||||||
|
void ipc_event_shutdown(void);
|
||||||
|
|
||||||
|
#endif /* LABWC_IPC_H */
|
||||||
|
|
@ -324,6 +324,8 @@ struct server {
|
||||||
pid_t primary_client_pid;
|
pid_t primary_client_pid;
|
||||||
|
|
||||||
char *title_fmt;
|
char *title_fmt;
|
||||||
|
|
||||||
|
struct wl_list ipc_clients;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* defined in main.c */
|
/* defined in main.c */
|
||||||
|
|
|
||||||
|
|
@ -242,6 +242,12 @@ struct view {
|
||||||
/* Set temporarily when moving view due to layout change */
|
/* Set temporarily when moving view due to layout change */
|
||||||
bool adjusting_for_layout_change;
|
bool adjusting_for_layout_change;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Last geometry reported to IPC subscribers. Used to detect
|
||||||
|
* actual position/size changes and emit move/resize events.
|
||||||
|
*/
|
||||||
|
struct wlr_box ipc_last_geo;
|
||||||
|
|
||||||
/* used by xdg-shell views */
|
/* used by xdg-shell views */
|
||||||
uint32_t pending_configure_serial;
|
uint32_t pending_configure_serial;
|
||||||
struct wl_event_source *pending_configure_timeout;
|
struct wl_event_source *pending_configure_timeout;
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@ input = dependency('libinput', version: '>=1.26', required: wlroots.get_variable
|
||||||
pixman = dependency('pixman-1')
|
pixman = dependency('pixman-1')
|
||||||
math = cc.find_library('m')
|
math = cc.find_library('m')
|
||||||
png = dependency('libpng')
|
png = dependency('libpng')
|
||||||
|
jsonc = dependency('json-c')
|
||||||
svg = dependency('librsvg-2.0', version: '>=2.46', required: false)
|
svg = dependency('librsvg-2.0', version: '>=2.46', required: false)
|
||||||
sfdo_basedir = dependency(
|
sfdo_basedir = dependency(
|
||||||
'libsfdo-basedir',
|
'libsfdo-basedir',
|
||||||
|
|
@ -174,6 +175,7 @@ labwc_deps = [
|
||||||
pixman,
|
pixman,
|
||||||
math,
|
math,
|
||||||
png,
|
png,
|
||||||
|
jsonc,
|
||||||
]
|
]
|
||||||
if have_rsvg
|
if have_rsvg
|
||||||
labwc_deps += [
|
labwc_deps += [
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,7 @@ action_arg_add_bool(struct action *action, const char *key, bool value)
|
||||||
wl_list_append(&action->args, &arg->base.link);
|
wl_list_append(&action->args, &arg->base.link);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
void
|
||||||
action_arg_add_int(struct action *action, const char *key, int value)
|
action_arg_add_int(struct action *action, const char *key, int value)
|
||||||
{
|
{
|
||||||
assert(action);
|
assert(action);
|
||||||
|
|
|
||||||
|
|
@ -2030,6 +2030,10 @@ rcxml_read(const char *filename)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!rc.loaded_config_file) {
|
||||||
|
xstrdup_replace(rc.loaded_config_file, path->string);
|
||||||
|
}
|
||||||
|
|
||||||
wlr_log(WLR_INFO, "read config file %s", path->string);
|
wlr_log(WLR_INFO, "read config file %s", path->string);
|
||||||
|
|
||||||
rcxml_parse_xml(&b);
|
rcxml_parse_xml(&b);
|
||||||
|
|
@ -2059,6 +2063,7 @@ rcxml_finish(void)
|
||||||
zfree(rc.workspace_config.initial_workspace_name);
|
zfree(rc.workspace_config.initial_workspace_name);
|
||||||
zfree(rc.tablet.output_name);
|
zfree(rc.tablet.output_name);
|
||||||
zfree(rc.window_switcher.osd.thumbnail_label_format);
|
zfree(rc.window_switcher.osd.thumbnail_label_format);
|
||||||
|
zfree(rc.loaded_config_file);
|
||||||
|
|
||||||
clear_title_layout();
|
clear_title_layout();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
#include "config/rcxml.h"
|
#include "config/rcxml.h"
|
||||||
#include "dnd.h"
|
#include "dnd.h"
|
||||||
#include "labwc.h"
|
#include "labwc.h"
|
||||||
|
#include "ipc.h"
|
||||||
#include "layers.h"
|
#include "layers.h"
|
||||||
#include "node.h"
|
#include "node.h"
|
||||||
#include "output.h"
|
#include "output.h"
|
||||||
|
|
@ -168,6 +169,8 @@ desktop_focus_view_internal(struct view *view, bool raise, bool allow_delay)
|
||||||
struct view *dialog = view_get_modal_dialog(view);
|
struct view *dialog = view_get_modal_dialog(view);
|
||||||
set_or_offer_focus(dialog ? dialog : view);
|
set_or_offer_focus(dialog ? dialog : view);
|
||||||
|
|
||||||
|
ipc_event_window("focus", view);
|
||||||
|
|
||||||
show_desktop_reset();
|
show_desktop_reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
#include "foreign-toplevel/foreign.h"
|
#include "foreign-toplevel/foreign.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h>
|
||||||
#include "common/mem.h"
|
#include "common/mem.h"
|
||||||
#include "foreign-toplevel/ext-foreign.h"
|
#include "foreign-toplevel/ext-foreign.h"
|
||||||
#include "foreign-toplevel/wlr-foreign.h"
|
#include "foreign-toplevel/wlr-foreign.h"
|
||||||
|
|
@ -27,7 +28,8 @@ foreign_toplevel_create(struct view *view)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
foreign_toplevel_set_parent(struct foreign_toplevel *toplevel, struct foreign_toplevel *parent)
|
foreign_toplevel_set_parent(struct foreign_toplevel *toplevel,
|
||||||
|
struct foreign_toplevel *parent)
|
||||||
{
|
{
|
||||||
assert(toplevel);
|
assert(toplevel);
|
||||||
wlr_foreign_toplevel_set_parent(&toplevel->wlr_toplevel,
|
wlr_foreign_toplevel_set_parent(&toplevel->wlr_toplevel,
|
||||||
|
|
@ -42,3 +44,17 @@ foreign_toplevel_destroy(struct foreign_toplevel *toplevel)
|
||||||
ext_foreign_toplevel_finish(&toplevel->ext_toplevel);
|
ext_foreign_toplevel_finish(&toplevel->ext_toplevel);
|
||||||
free(toplevel);
|
free(toplevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
foreign_toplevel_get_identifier(struct foreign_toplevel *toplevel)
|
||||||
|
{
|
||||||
|
if (!toplevel) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
struct wlr_ext_foreign_toplevel_handle_v1 *handle =
|
||||||
|
toplevel->ext_toplevel.handle;
|
||||||
|
if (handle && handle->identifier) {
|
||||||
|
return handle->identifier;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ labwc_sources = files(
|
||||||
'edges.c',
|
'edges.c',
|
||||||
'idle.c',
|
'idle.c',
|
||||||
'interactive.c',
|
'interactive.c',
|
||||||
|
'ipc.c',
|
||||||
'layers.c',
|
'layers.c',
|
||||||
'magnifier.c',
|
'magnifier.c',
|
||||||
'main.c',
|
'main.c',
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@
|
||||||
#include "common/string-helpers.h"
|
#include "common/string-helpers.h"
|
||||||
#include "config/rcxml.h"
|
#include "config/rcxml.h"
|
||||||
#include "labwc.h"
|
#include "labwc.h"
|
||||||
|
#include "ipc.h"
|
||||||
#include "layers.h"
|
#include "layers.h"
|
||||||
#include "node.h"
|
#include "node.h"
|
||||||
#include "output-state.h"
|
#include "output-state.h"
|
||||||
|
|
@ -285,6 +286,7 @@ handle_output_destroy(struct wl_listener *listener, void *data)
|
||||||
wl_list_remove(&output->destroy.link);
|
wl_list_remove(&output->destroy.link);
|
||||||
wl_list_remove(&output->request_state.link);
|
wl_list_remove(&output->request_state.link);
|
||||||
seat_output_layout_changed(seat);
|
seat_output_layout_changed(seat);
|
||||||
|
ipc_event_output("destroy");
|
||||||
|
|
||||||
for (size_t i = 0; i < ARRAY_SIZE(output->layer_tree); i++) {
|
for (size_t i = 0; i < ARRAY_SIZE(output->layer_tree); i++) {
|
||||||
wlr_scene_node_destroy(&output->layer_tree[i]->node);
|
wlr_scene_node_destroy(&output->layer_tree[i]->node);
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
#include "common/border.h"
|
#include "common/border.h"
|
||||||
#include "common/lab-scene-rect.h"
|
#include "common/lab-scene-rect.h"
|
||||||
#include "config/rcxml.h"
|
#include "config/rcxml.h"
|
||||||
|
#include "ipc.h"
|
||||||
#include "resize-indicator.h"
|
#include "resize-indicator.h"
|
||||||
#include "ssd.h"
|
#include "ssd.h"
|
||||||
#include "theme.h"
|
#include "theme.h"
|
||||||
|
|
@ -50,6 +51,9 @@ resize_outlines_update(struct view *view, struct wlr_box new_geo)
|
||||||
|
|
||||||
outlines->view_geo = new_geo;
|
outlines->view_geo = new_geo;
|
||||||
|
|
||||||
|
/* Required in case <resize drawContents="no" /> is used */
|
||||||
|
ipc_event_window_geometry(view, &new_geo);
|
||||||
|
|
||||||
resize_indicator_update(view);
|
resize_indicator_update(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@
|
||||||
#include "desktop-entry.h"
|
#include "desktop-entry.h"
|
||||||
#include "idle.h"
|
#include "idle.h"
|
||||||
#include "input/keyboard.h"
|
#include "input/keyboard.h"
|
||||||
|
#include "ipc.h"
|
||||||
#include "labwc.h"
|
#include "labwc.h"
|
||||||
#include "layers.h"
|
#include "layers.h"
|
||||||
#include "magnifier.h"
|
#include "magnifier.h"
|
||||||
|
|
@ -833,6 +834,8 @@ server_start(void)
|
||||||
/* Potentially set up the initial fallback output */
|
/* Potentially set up the initial fallback output */
|
||||||
output_virtual_update_fallback();
|
output_virtual_update_fallback();
|
||||||
|
|
||||||
|
ipc_init();
|
||||||
|
|
||||||
if (setenv("WAYLAND_DISPLAY", socket, true) < 0) {
|
if (setenv("WAYLAND_DISPLAY", socket, true) < 0) {
|
||||||
wlr_log_errno(WLR_ERROR, "unable to set WAYLAND_DISPLAY");
|
wlr_log_errno(WLR_ERROR, "unable to set WAYLAND_DISPLAY");
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -881,5 +884,7 @@ server_finish(void)
|
||||||
workspaces_destroy();
|
workspaces_destroy();
|
||||||
wlr_scene_node_destroy(&server.scene->tree.node);
|
wlr_scene_node_destroy(&server.scene->tree.node);
|
||||||
|
|
||||||
|
ipc_finish();
|
||||||
|
|
||||||
wl_display_destroy(server.wl_display);
|
wl_display_destroy(server.wl_display);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
22
src/view.c
22
src/view.c
|
|
@ -19,6 +19,7 @@
|
||||||
#include "cycle.h"
|
#include "cycle.h"
|
||||||
#include "foreign-toplevel/foreign.h"
|
#include "foreign-toplevel/foreign.h"
|
||||||
#include "input/keyboard.h"
|
#include "input/keyboard.h"
|
||||||
|
#include "ipc.h"
|
||||||
#include "labwc.h"
|
#include "labwc.h"
|
||||||
#include "menu/menu.h"
|
#include "menu/menu.h"
|
||||||
#include "output.h"
|
#include "output.h"
|
||||||
|
|
@ -567,6 +568,17 @@ view_moved(struct view *view)
|
||||||
if (rc.resize_indicator && server.grabbed_view == view) {
|
if (rc.resize_indicator && server.grabbed_view == view) {
|
||||||
resize_indicator_update(view);
|
resize_indicator_update(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fallback IPC emission: catch geometry corrections made by
|
||||||
|
* the client on commit (e.g. terminal snapping to char grid).
|
||||||
|
* The primary emission point is in view_move_resize() which
|
||||||
|
* fires immediately using view->pending. Events are
|
||||||
|
* deduplicated in ipc_event_window_geometry().
|
||||||
|
*/
|
||||||
|
if (view->mapped) {
|
||||||
|
ipc_event_window_geometry(view, &view->current);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -590,6 +602,16 @@ view_move_resize(struct view *view, struct wlr_box geo)
|
||||||
if (!view->adjusting_for_layout_change) {
|
if (!view->adjusting_for_layout_change) {
|
||||||
view_save_last_placement(view);
|
view_save_last_placement(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Emit IPC move/resize events based on pending geometry.
|
||||||
|
* This fires immediately when the resize is requested rather
|
||||||
|
* than waiting for the client to commit, giving subscribers
|
||||||
|
* realtime tracking that matches interactive move behaviour.
|
||||||
|
*/
|
||||||
|
if (view->mapped) {
|
||||||
|
ipc_event_window_geometry(view, &view->pending);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
#include "config/rcxml.h"
|
#include "config/rcxml.h"
|
||||||
#include "input/keyboard.h"
|
#include "input/keyboard.h"
|
||||||
#include "labwc.h"
|
#include "labwc.h"
|
||||||
|
#include "ipc.h"
|
||||||
#include "output.h"
|
#include "output.h"
|
||||||
#include "show-desktop.h"
|
#include "show-desktop.h"
|
||||||
#include "theme.h"
|
#include "theme.h"
|
||||||
|
|
@ -497,6 +498,8 @@ workspaces_switch_to(struct workspace *target, bool update_focus)
|
||||||
|
|
||||||
wlr_ext_workspace_handle_v1_set_active(target->ext_workspace, true);
|
wlr_ext_workspace_handle_v1_set_active(target->ext_workspace, true);
|
||||||
|
|
||||||
|
ipc_event_workspace("focus", target, server.workspaces.last);
|
||||||
|
|
||||||
show_desktop_reset();
|
show_desktop_reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
#include "config/rcxml.h"
|
#include "config/rcxml.h"
|
||||||
#include "decorations.h"
|
#include "decorations.h"
|
||||||
#include "foreign-toplevel/foreign.h"
|
#include "foreign-toplevel/foreign.h"
|
||||||
|
#include "ipc.h"
|
||||||
#include "labwc.h"
|
#include "labwc.h"
|
||||||
#include "menu/menu.h"
|
#include "menu/menu.h"
|
||||||
#include "node.h"
|
#include "node.h"
|
||||||
|
|
@ -592,6 +593,7 @@ handle_set_title(struct wl_listener *listener, void *data)
|
||||||
struct wlr_xdg_toplevel *toplevel = xdg_toplevel_from_view(view);
|
struct wlr_xdg_toplevel *toplevel = xdg_toplevel_from_view(view);
|
||||||
|
|
||||||
view_set_title(view, toplevel->title);
|
view_set_title(view, toplevel->title);
|
||||||
|
ipc_event_window("title", view);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -884,6 +886,8 @@ handle_map(struct wl_listener *listener, void *data)
|
||||||
|
|
||||||
view_impl_map(view);
|
view_impl_map(view);
|
||||||
view->been_mapped = true;
|
view->been_mapped = true;
|
||||||
|
ipc_event_window("new", view);
|
||||||
|
view->ipc_last_geo = view->current;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -893,6 +897,7 @@ handle_unmap(struct wl_listener *listener, void *data)
|
||||||
if (view->mapped) {
|
if (view->mapped) {
|
||||||
view->mapped = false;
|
view->mapped = false;
|
||||||
view_impl_unmap(view);
|
view_impl_unmap(view);
|
||||||
|
ipc_event_window("close", view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
#include "config/rcxml.h"
|
#include "config/rcxml.h"
|
||||||
#include "config/session.h"
|
#include "config/session.h"
|
||||||
#include "foreign-toplevel/foreign.h"
|
#include "foreign-toplevel/foreign.h"
|
||||||
|
#include "ipc.h"
|
||||||
#include "labwc.h"
|
#include "labwc.h"
|
||||||
#include "node.h"
|
#include "node.h"
|
||||||
#include "output.h"
|
#include "output.h"
|
||||||
|
|
@ -580,6 +581,7 @@ handle_set_title(struct wl_listener *listener, void *data)
|
||||||
struct view *view = wl_container_of(listener, view, set_title);
|
struct view *view = wl_container_of(listener, view, set_title);
|
||||||
struct xwayland_view *xwayland_view = xwayland_view_from_view(view);
|
struct xwayland_view *xwayland_view = xwayland_view_from_view(view);
|
||||||
view_set_title(view, xwayland_view->xwayland_surface->title);
|
view_set_title(view, xwayland_view->xwayland_surface->title);
|
||||||
|
ipc_event_window("title", view);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -825,6 +827,8 @@ handle_map(struct wl_listener *listener, void *data)
|
||||||
|
|
||||||
view_impl_map(view);
|
view_impl_map(view);
|
||||||
view->been_mapped = true;
|
view->been_mapped = true;
|
||||||
|
ipc_event_window("new", view);
|
||||||
|
view->ipc_last_geo = view->current;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -836,6 +840,7 @@ handle_unmap(struct wl_listener *listener, void *data)
|
||||||
}
|
}
|
||||||
view->mapped = false;
|
view->mapped = false;
|
||||||
view_impl_unmap(view);
|
view_impl_unmap(view);
|
||||||
|
ipc_event_window("close", view);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Destroy the content_tree at unmap. Alternatively, we could
|
* Destroy the content_tree at unmap. Alternatively, we could
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue