2019-11-01 20:37:22 +01:00
|
|
|
#include "server.h"
|
|
|
|
|
|
2019-12-01 19:22:45 +01:00
|
|
|
#include <string.h>
|
2022-01-15 16:58:32 +01:00
|
|
|
#include <fcntl.h>
|
2019-11-01 20:37:22 +01:00
|
|
|
#include <unistd.h>
|
2019-11-02 23:25:14 +01:00
|
|
|
#include <errno.h>
|
2025-02-13 16:16:43 +01:00
|
|
|
#include <limits.h>
|
2019-11-01 20:37:22 +01:00
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/socket.h>
|
|
|
|
|
#include <sys/epoll.h>
|
|
|
|
|
|
2021-01-19 14:34:30 +00:00
|
|
|
#include <sys/un.h>
|
2019-11-01 20:37:22 +01:00
|
|
|
|
2019-11-17 19:19:55 +01:00
|
|
|
#include <tllist.h>
|
|
|
|
|
|
2019-11-01 20:37:22 +01:00
|
|
|
#define LOG_MODULE "server"
|
2019-11-05 14:36:58 +01:00
|
|
|
#define LOG_ENABLE_DBG 0
|
2019-11-01 20:37:22 +01:00
|
|
|
#include "log.h"
|
2019-11-02 00:49:25 +01:00
|
|
|
|
2020-11-21 20:21:18 +01:00
|
|
|
#include "client-protocol.h"
|
2019-11-01 20:37:22 +01:00
|
|
|
#include "terminal.h"
|
2022-01-15 16:58:32 +01:00
|
|
|
#include "util.h"
|
2019-11-02 00:49:25 +01:00
|
|
|
#include "wayland.h"
|
2020-08-08 20:34:30 +01:00
|
|
|
#include "xmalloc.h"
|
2019-11-01 20:37:22 +01:00
|
|
|
|
2025-02-13 16:16:43 +01:00
|
|
|
#define NON_ZERO_OPT (INT_MIN / 7)
|
|
|
|
|
|
2019-11-01 20:37:22 +01:00
|
|
|
struct client;
|
2021-03-12 20:46:55 -03:00
|
|
|
struct terminal_instance;
|
|
|
|
|
|
2019-11-01 20:37:22 +01:00
|
|
|
struct server {
|
2025-07-30 12:23:39 +02:00
|
|
|
struct config *conf;
|
2019-11-01 20:37:22 +01:00
|
|
|
struct fdm *fdm;
|
2020-05-21 20:17:29 +02:00
|
|
|
struct reaper *reaper;
|
2019-11-01 20:37:22 +01:00
|
|
|
struct wayland *wayl;
|
|
|
|
|
|
|
|
|
|
int fd;
|
2019-12-14 12:59:38 +01:00
|
|
|
const char *sock_path;
|
2019-11-01 20:37:22 +01:00
|
|
|
|
|
|
|
|
tll(struct client *) clients;
|
2021-03-12 20:46:55 -03:00
|
|
|
tll(struct terminal_instance *) terminals;
|
2019-11-01 20:37:22 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct client {
|
|
|
|
|
struct server *server;
|
|
|
|
|
int fd;
|
|
|
|
|
|
2019-11-05 10:08:30 +01:00
|
|
|
struct {
|
|
|
|
|
uint8_t *data;
|
|
|
|
|
size_t left;
|
|
|
|
|
size_t idx;
|
|
|
|
|
} buffer;
|
|
|
|
|
|
2021-03-12 20:46:55 -03:00
|
|
|
struct terminal_instance *instance;
|
|
|
|
|
};
|
2021-03-13 07:41:05 -03:00
|
|
|
static void client_destroy(struct client *client);
|
2021-03-12 20:46:55 -03:00
|
|
|
|
|
|
|
|
struct terminal_instance {
|
|
|
|
|
struct terminal *terminal;
|
|
|
|
|
struct server *server;
|
|
|
|
|
struct client *client;
|
2021-06-17 18:11:16 +02:00
|
|
|
struct config *conf;
|
2019-11-01 20:37:22 +01:00
|
|
|
};
|
2021-03-13 07:41:05 -03:00
|
|
|
static void instance_destroy(struct terminal_instance *instance, int exit_code);
|
2019-11-01 20:37:22 +01:00
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
client_destroy(struct client *client)
|
|
|
|
|
{
|
|
|
|
|
if (client == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
2021-03-12 20:46:55 -03:00
|
|
|
if (client->instance != NULL) {
|
2019-11-01 20:37:22 +01:00
|
|
|
LOG_WARN("client FD=%d: terminal still alive", client->fd);
|
2021-03-12 20:46:55 -03:00
|
|
|
client->instance->client = NULL;
|
|
|
|
|
instance_destroy(client->instance, 1);
|
2019-11-01 20:37:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client->fd != -1) {
|
|
|
|
|
LOG_DBG("client FD=%d: disconnected", client->fd);
|
|
|
|
|
fdm_del(client->server->fdm, client->fd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tll_foreach(client->server->clients, it) {
|
|
|
|
|
if (it->item == client) {
|
|
|
|
|
tll_remove(client->server->clients, it);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-05 10:08:30 +01:00
|
|
|
free(client->buffer.data);
|
2019-11-01 20:37:22 +01:00
|
|
|
free(client);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
client_send_exit_code(struct client *client, int exit_code)
|
|
|
|
|
{
|
|
|
|
|
if (client->fd == -1)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (write(client->fd, &exit_code, sizeof(exit_code)) != sizeof(exit_code))
|
|
|
|
|
LOG_ERRNO("failed to write slave exit code to client");
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-12 20:46:55 -03:00
|
|
|
static void
|
|
|
|
|
instance_destroy(struct terminal_instance *instance, int exit_code)
|
|
|
|
|
{
|
2021-03-13 07:41:05 -03:00
|
|
|
if (instance->terminal != NULL)
|
2021-03-12 20:46:55 -03:00
|
|
|
term_destroy(instance->terminal);
|
|
|
|
|
|
|
|
|
|
tll_foreach(instance->server->terminals, it) {
|
|
|
|
|
if (it->item == instance) {
|
|
|
|
|
tll_remove(instance->server->terminals, it);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (instance->client != NULL) {
|
|
|
|
|
instance->client->instance = NULL;
|
|
|
|
|
client_send_exit_code(instance->client, exit_code);
|
|
|
|
|
client_destroy(instance->client);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* TODO: clone server conf completely, so that we can just call
|
|
|
|
|
* conf_destroy() here */
|
2021-06-17 18:11:16 +02:00
|
|
|
if (instance->conf != NULL) {
|
2022-04-12 13:01:56 +02:00
|
|
|
config_free(instance->conf);
|
2021-06-23 13:18:55 +02:00
|
|
|
free(instance->conf);
|
2021-06-17 18:11:16 +02:00
|
|
|
}
|
2021-03-12 20:46:55 -03:00
|
|
|
free(instance);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-01 20:37:22 +01:00
|
|
|
static void
|
|
|
|
|
term_shutdown_handler(void *data, int exit_code)
|
|
|
|
|
{
|
2021-03-12 20:46:55 -03:00
|
|
|
struct terminal_instance *instance = data;
|
2019-11-01 20:37:22 +01:00
|
|
|
|
2021-03-12 20:46:55 -03:00
|
|
|
instance->terminal = NULL;
|
|
|
|
|
instance_destroy(instance, exit_code);
|
2019-11-01 20:37:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
fdm_client(struct fdm *fdm, int fd, int events, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct client *client = data;
|
|
|
|
|
struct server *server = client->server;
|
2019-11-05 09:31:14 +01:00
|
|
|
|
|
|
|
|
char **argv = NULL;
|
2021-06-23 15:38:29 +02:00
|
|
|
config_override_t overrides = tll_init();
|
2022-04-11 12:19:40 +02:00
|
|
|
char **envp = NULL;
|
2019-11-01 20:37:22 +01:00
|
|
|
|
|
|
|
|
if (events & EPOLLHUP)
|
|
|
|
|
goto shutdown;
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
xassert(events & EPOLLIN);
|
2019-11-01 20:37:22 +01:00
|
|
|
|
2021-03-12 20:46:55 -03:00
|
|
|
if (client->instance != NULL) {
|
2025-07-31 17:37:19 +02:00
|
|
|
struct client_ipc_hdr ipc_hdr;
|
|
|
|
|
ssize_t count = read(fd, &ipc_hdr, sizeof(ipc_hdr));
|
|
|
|
|
|
|
|
|
|
if (count != sizeof(ipc_hdr)) {
|
|
|
|
|
LOG_WARN("client unexpectedly sent %zd bytes", count);
|
|
|
|
|
return true; /* TODO: shutdown instead? */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (ipc_hdr.ipc_code) {
|
|
|
|
|
case FOOT_IPC_SIGUSR: {
|
|
|
|
|
xassert(ipc_hdr.size == sizeof(struct client_ipc_sigusr));
|
|
|
|
|
|
|
|
|
|
struct client_ipc_sigusr sigusr;
|
|
|
|
|
count = read(fd, &sigusr, sizeof(sigusr));
|
|
|
|
|
if (count < 0) {
|
|
|
|
|
LOG_ERRNO("failed to read SIGUSR IPC data from client");
|
|
|
|
|
return true; /* TODO: shutdown instead? */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((size_t)count != sizeof(sigusr)) {
|
|
|
|
|
LOG_ERR("failed to read SIGUSR IPC data from client");
|
|
|
|
|
return true; /* TODO: shutdown instead? */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (sigusr.signo) {
|
|
|
|
|
case SIGUSR1:
|
config: add [colors-dark] and [colors-light], replacing [colors] and [colors2]
The main reason for having two color sections is to be able to switch
between dark and light. Thus, it's better if the section names reflect
this, rather than the more generic 'colors' and 'colors2' (which was
the dark one and which was the light one, now again?)
When the second color section was added, we kept the original name,
colors, to make sure we didn't break existing configurations, and
third-party themes.
However, in the long run, it's probably better to be specific in the
section naming, to avoid confusion.
So, add 'colors-dark', and 'colors-light'. Keep 'colors' and 'colors2'
as aliases for now, but mark them as deprecated. They WILL be removed
in a future release.
Also rename the option values for initial-color-theme, from 1/2, to
dark/light. Keep the old ones for now, marked as deprecated.
Update all bundled themes to use the new names. In the light-only
themes (i.e. themes that define a single, light, theme), use
colors-light, and set initial-color-theme=light.
Possible improvements: disable color switching if only one color
section has been explicitly configured (todo: figure out how to handle
the default color theme values...)
2025-12-19 09:29:06 +01:00
|
|
|
term_theme_switch_to_dark(client->instance->terminal);
|
2025-07-31 17:37:19 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SIGUSR2:
|
config: add [colors-dark] and [colors-light], replacing [colors] and [colors2]
The main reason for having two color sections is to be able to switch
between dark and light. Thus, it's better if the section names reflect
this, rather than the more generic 'colors' and 'colors2' (which was
the dark one and which was the light one, now again?)
When the second color section was added, we kept the original name,
colors, to make sure we didn't break existing configurations, and
third-party themes.
However, in the long run, it's probably better to be specific in the
section naming, to avoid confusion.
So, add 'colors-dark', and 'colors-light'. Keep 'colors' and 'colors2'
as aliases for now, but mark them as deprecated. They WILL be removed
in a future release.
Also rename the option values for initial-color-theme, from 1/2, to
dark/light. Keep the old ones for now, marked as deprecated.
Update all bundled themes to use the new names. In the light-only
themes (i.e. themes that define a single, light, theme), use
colors-light, and set initial-color-theme=light.
Possible improvements: disable color switching if only one color
section has been explicitly configured (todo: figure out how to handle
the default color theme values...)
2025-12-19 09:29:06 +01:00
|
|
|
term_theme_switch_to_light(client->instance->terminal);
|
2025-07-31 17:37:19 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
LOG_ERR(
|
|
|
|
|
"client sent bad SIGUSR number: %d "
|
|
|
|
|
"(expected SIGUSR1=%d or SIGUSR2=%d)",
|
|
|
|
|
sigusr.signo, SIGUSR1, SIGUSR2);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
LOG_WARN(
|
|
|
|
|
"client sent unrecognized IPC (0x%04x), ignoring %hhu bytes",
|
|
|
|
|
ipc_hdr.ipc_code, ipc_hdr.size);
|
|
|
|
|
|
|
|
|
|
/* TODO: slightly broken, since not all data is guaranteed
|
|
|
|
|
to be readable yet */
|
|
|
|
|
uint8_t dummy[ipc_hdr.size];
|
2025-08-01 09:41:37 +02:00
|
|
|
(void)!!read(fd, dummy, ipc_hdr.size);
|
2025-07-31 17:37:19 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
2019-11-05 10:08:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client->buffer.data == NULL) {
|
|
|
|
|
/*
|
|
|
|
|
* We haven't received any data yet - the first thing the
|
|
|
|
|
* client sends is the total size of the initialization
|
|
|
|
|
* data.
|
|
|
|
|
*/
|
|
|
|
|
uint32_t total_len;
|
2020-01-10 19:27:17 +01:00
|
|
|
ssize_t count = recv(fd, &total_len, sizeof(total_len), 0);
|
|
|
|
|
if (count < 0) {
|
2019-11-05 10:08:30 +01:00
|
|
|
LOG_ERRNO("failed to read total length");
|
|
|
|
|
goto shutdown;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-10 19:27:17 +01:00
|
|
|
if (count != sizeof(total_len)) {
|
|
|
|
|
LOG_ERR("client did not send setup packet size");
|
|
|
|
|
goto shutdown;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const uint32_t max_size = 128 * 1024;
|
|
|
|
|
if (total_len > max_size) {
|
|
|
|
|
LOG_ERR("client wants to send too large setup packet (%u > %u)",
|
|
|
|
|
total_len, max_size);
|
|
|
|
|
goto shutdown;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-05 10:08:30 +01:00
|
|
|
LOG_DBG("total len: %u", total_len);
|
2020-08-08 20:34:30 +01:00
|
|
|
client->buffer.data = xmalloc(total_len + 1);
|
2019-11-05 10:08:30 +01:00
|
|
|
client->buffer.left = total_len;
|
|
|
|
|
client->buffer.idx = 0;
|
|
|
|
|
|
|
|
|
|
/* Prevent our strlen() calls to run outside */
|
|
|
|
|
client->buffer.data[total_len] = '\0';
|
|
|
|
|
return true; /* Let FDM trigger again when we have more data */
|
2019-11-05 09:31:47 +01:00
|
|
|
}
|
|
|
|
|
|
2020-01-10 19:49:26 +01:00
|
|
|
/* Keep filling our buffer of initialization data */
|
|
|
|
|
ssize_t count = recv(
|
|
|
|
|
fd, &client->buffer.data[client->buffer.idx], client->buffer.left, 0);
|
2019-11-05 10:08:30 +01:00
|
|
|
|
2020-01-10 19:49:26 +01:00
|
|
|
if (count < 0) {
|
|
|
|
|
LOG_ERRNO("failed to read");
|
|
|
|
|
goto shutdown;
|
|
|
|
|
}
|
2019-11-01 21:10:47 +01:00
|
|
|
|
2020-01-10 19:49:26 +01:00
|
|
|
client->buffer.idx += count;
|
|
|
|
|
client->buffer.left -= count;
|
2019-11-05 10:08:30 +01:00
|
|
|
|
2020-01-10 19:49:26 +01:00
|
|
|
if (client->buffer.left > 0) {
|
|
|
|
|
/* Not done yet */
|
|
|
|
|
return true;
|
2019-11-02 12:17:30 +01:00
|
|
|
}
|
2019-11-01 21:10:47 +01:00
|
|
|
|
2025-02-05 14:23:17 +03:00
|
|
|
if (tll_length(server->wayl->monitors) == 0) {
|
|
|
|
|
LOG_ERR("no monitors available for new terminal");
|
|
|
|
|
client_send_exit_code(client, -26);
|
|
|
|
|
goto shutdown;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-05 10:08:30 +01:00
|
|
|
/* All initialization data received - time to instantiate a terminal! */
|
|
|
|
|
|
2021-03-12 20:46:55 -03:00
|
|
|
xassert(client->instance == NULL);
|
2021-01-16 20:16:00 +00:00
|
|
|
xassert(client->buffer.data != NULL);
|
|
|
|
|
xassert(client->buffer.left == 0);
|
2019-11-05 10:08:30 +01:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Parse the received buffer, verifying lengths etc
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#define CHECK_BUF(sz) do { \
|
|
|
|
|
if (p + (sz) > end) \
|
|
|
|
|
goto shutdown; \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
2020-11-21 20:27:13 +01:00
|
|
|
#define CHECK_BUF_AND_NULL(sz) do { \
|
|
|
|
|
CHECK_BUF(sz); \
|
|
|
|
|
if (sz == 0) \
|
|
|
|
|
goto shutdown; \
|
|
|
|
|
if (p[sz - 1] != '\0') \
|
|
|
|
|
goto shutdown; \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
2019-11-05 10:08:30 +01:00
|
|
|
uint8_t *p = client->buffer.data;
|
|
|
|
|
const uint8_t *end = &client->buffer.data[client->buffer.idx];
|
|
|
|
|
|
2020-11-21 20:47:17 +01:00
|
|
|
struct client_data cdata;
|
|
|
|
|
CHECK_BUF(sizeof(cdata));
|
|
|
|
|
memcpy(&cdata, p, sizeof(cdata));
|
|
|
|
|
p += sizeof(cdata);
|
2019-12-21 19:56:37 +01:00
|
|
|
|
2020-11-21 20:47:17 +01:00
|
|
|
CHECK_BUF_AND_NULL(cdata.cwd_len);
|
|
|
|
|
const char *cwd = (const char *)p; p += cdata.cwd_len;
|
|
|
|
|
LOG_DBG("CWD = %.*s", cdata.cwd_len, cwd);
|
2019-12-21 19:56:37 +01:00
|
|
|
|
2021-10-28 17:51:44 -07:00
|
|
|
/* XDGA token */
|
|
|
|
|
const char *token = NULL;
|
|
|
|
|
if (cdata.xdga_token) {
|
|
|
|
|
|
|
|
|
|
CHECK_BUF_AND_NULL(cdata.token_len);
|
|
|
|
|
token = (const char *)p; p += cdata.token_len;
|
|
|
|
|
LOG_DBG("XDGA = %.*s", cdata.token_len, token);
|
|
|
|
|
} else {
|
|
|
|
|
LOG_DBG("No XDGA token");
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-23 14:22:18 +02:00
|
|
|
/* Overrides */
|
|
|
|
|
for (uint16_t i = 0; i < cdata.override_count; i++) {
|
2021-06-23 14:30:02 +02:00
|
|
|
struct client_string arg;
|
|
|
|
|
CHECK_BUF(sizeof(arg));
|
|
|
|
|
memcpy(&arg, p, sizeof(arg)); p += sizeof(arg);
|
2020-04-01 19:59:47 +02:00
|
|
|
|
2021-06-23 14:30:02 +02:00
|
|
|
CHECK_BUF_AND_NULL(arg.len);
|
2021-06-23 14:22:18 +02:00
|
|
|
const char *str = (const char *)p;
|
2021-06-23 14:30:02 +02:00
|
|
|
p += arg.len;
|
2020-04-01 19:59:47 +02:00
|
|
|
|
2021-06-23 14:22:18 +02:00
|
|
|
tll_push_back(overrides, xstrdup(str));
|
|
|
|
|
}
|
2020-04-01 19:59:47 +02:00
|
|
|
|
2021-06-23 14:22:18 +02:00
|
|
|
/* argv */
|
2020-11-21 20:47:17 +01:00
|
|
|
argv = xcalloc(cdata.argc + 1, sizeof(argv[0]));
|
|
|
|
|
for (uint16_t i = 0; i < cdata.argc; i++) {
|
2021-06-23 14:22:18 +02:00
|
|
|
struct client_string arg;
|
2020-11-21 20:47:17 +01:00
|
|
|
CHECK_BUF(sizeof(arg));
|
2021-06-23 14:30:02 +02:00
|
|
|
memcpy(&arg, p, sizeof(arg)); p += sizeof(arg);
|
2020-04-01 18:40:51 +02:00
|
|
|
|
2020-11-21 20:47:17 +01:00
|
|
|
CHECK_BUF_AND_NULL(arg.len);
|
|
|
|
|
argv[i] = (char *)p; p += arg.len;
|
|
|
|
|
LOG_DBG("argv[%hu] = %.*s", i, arg.len, argv[i]);
|
2019-11-01 20:37:22 +01:00
|
|
|
}
|
2022-04-11 12:19:40 +02:00
|
|
|
|
|
|
|
|
/* envp */
|
|
|
|
|
envp = cdata.env_count != 0
|
|
|
|
|
? xcalloc(cdata.env_count + 1, sizeof(envp[0]))
|
|
|
|
|
: NULL;
|
|
|
|
|
|
|
|
|
|
for (uint16_t i = 0; i < cdata.env_count; i++) {
|
|
|
|
|
struct client_string e;
|
|
|
|
|
CHECK_BUF(sizeof(e));
|
|
|
|
|
memcpy(&e, p, sizeof(e)); p += sizeof(e);
|
|
|
|
|
|
|
|
|
|
CHECK_BUF_AND_NULL(e.len);
|
|
|
|
|
envp[i] = (char *)p; p += e.len;
|
|
|
|
|
LOG_DBG("env[%hu] = %.*s", i, e.len, envp[i]);
|
|
|
|
|
}
|
2019-11-05 10:08:30 +01:00
|
|
|
|
2020-11-21 20:27:13 +01:00
|
|
|
#undef CHECK_BUF_AND_NULL
|
2019-11-05 10:08:30 +01:00
|
|
|
#undef CHECK_BUF
|
2019-11-01 20:37:22 +01:00
|
|
|
|
2024-01-25 07:03:50 +00:00
|
|
|
struct terminal_instance *instance = xmalloc(sizeof(struct terminal_instance));
|
2021-03-12 20:46:55 -03:00
|
|
|
|
2021-06-17 18:11:16 +02:00
|
|
|
const bool need_to_clone_conf =
|
2021-06-23 14:22:18 +02:00
|
|
|
tll_length(overrides)> 0 ||
|
2021-09-05 09:28:19 +02:00
|
|
|
cdata.hold != server->conf->hold_at_exit;
|
2021-06-17 18:11:16 +02:00
|
|
|
|
|
|
|
|
struct config *conf = NULL;
|
|
|
|
|
if (need_to_clone_conf) {
|
2021-06-23 13:18:55 +02:00
|
|
|
conf = config_clone(server->conf);
|
|
|
|
|
|
|
|
|
|
if (cdata.hold != server->conf->hold_at_exit)
|
|
|
|
|
conf->hold_at_exit = cdata.hold;
|
|
|
|
|
|
|
|
|
|
config_override_apply(conf, &overrides, false);
|
2021-08-31 19:52:31 +02:00
|
|
|
|
|
|
|
|
if (conf->tweak.font_monospace_warn && conf->fonts[0].count > 0) {
|
|
|
|
|
check_if_font_is_monospaced(
|
|
|
|
|
conf->fonts[0].arr[0].pattern,
|
|
|
|
|
&conf->notifications);
|
|
|
|
|
}
|
2021-06-17 18:11:16 +02:00
|
|
|
}
|
|
|
|
|
|
2021-03-12 20:46:55 -03:00
|
|
|
*instance = (struct terminal_instance) {
|
2021-03-13 07:41:05 -03:00
|
|
|
.client = NULL,
|
2021-03-12 20:46:55 -03:00
|
|
|
.server = server,
|
2021-06-17 18:11:16 +02:00
|
|
|
.conf = conf,
|
2021-03-12 20:46:55 -03:00
|
|
|
};
|
2020-11-22 00:12:23 +00:00
|
|
|
|
2021-03-12 20:46:55 -03:00
|
|
|
instance->terminal = term_init(
|
2021-06-17 18:11:16 +02:00
|
|
|
conf != NULL ? conf : server->conf,
|
2021-10-28 17:51:44 -07:00
|
|
|
server->fdm, server->reaper, server->wayl, "footclient", cwd, token,
|
2021-12-10 17:40:59 +00:00
|
|
|
NULL, cdata.argc, argv, (const char *const *)envp,
|
slave: don't skip setting environment variables when using a custom environment
When launching footclient with -E,--client-environment the environment
variables that should be set by foot, wasn't.
Those variables are:
* TERM
* COLORTERM
* PWD
* SHELL
and all variables defined by the user in the [environment] section in
foot.ini.
In the same way, we did not *unset* TERM_PROGRAM and
TERM_PROGRAM_VERSION.
This patch fixes it by "cloning" the custom environment, making it
mutable, and then adding/removing the variables above from it.
Instead of calling setenv()/unsetenv() directly, we add the wrapper
functions add_to_env() and del_from_env().
When *not* using a custom environment, they simply call
setenv()/unsetenv().
When we *are* using a custom environment, add_to_env() first loops all
existing variables, looking for a match. If a match is found, it's
updated with the new value. If it's not found, a new entry is added.
del_from_env() loops all entries, and removes it when a match is
found. If no match is found, nothing is done.
The mutable environment is allocated on the heap, but never free:d. We
don't need to free it, since it's only allocated after forking, in the
child process.
Closes #1568
2024-01-09 16:50:47 +01:00
|
|
|
&term_shutdown_handler, instance);
|
2019-11-01 20:37:22 +01:00
|
|
|
|
2021-03-12 20:46:55 -03:00
|
|
|
if (instance->terminal == NULL) {
|
2019-11-01 20:37:22 +01:00
|
|
|
LOG_ERR("failed to instantiate new terminal");
|
2021-05-01 10:46:40 +02:00
|
|
|
client_send_exit_code(client, -26);
|
2021-03-12 20:46:55 -03:00
|
|
|
instance_destroy(instance, -1);
|
2019-11-01 20:37:22 +01:00
|
|
|
goto shutdown;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-05 09:28:19 +02:00
|
|
|
if (cdata.no_wait) {
|
2021-03-12 20:46:55 -03:00
|
|
|
// the server owns the instance
|
|
|
|
|
tll_push_back(server->terminals, instance);
|
|
|
|
|
client_send_exit_code(client, 0);
|
|
|
|
|
goto shutdown;
|
|
|
|
|
} else {
|
|
|
|
|
// the instance is attached to the client
|
|
|
|
|
instance->client = client;
|
|
|
|
|
client->instance = instance;
|
2021-03-13 07:41:05 -03:00
|
|
|
free(argv);
|
2022-04-11 12:19:40 +02:00
|
|
|
free(envp);
|
2021-06-23 14:22:18 +02:00
|
|
|
tll_free_and_free(overrides, free);
|
2021-03-12 20:46:55 -03:00
|
|
|
}
|
|
|
|
|
|
2019-11-01 20:37:22 +01:00
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
shutdown:
|
|
|
|
|
LOG_DBG("client FD=%d: disconnected", client->fd);
|
|
|
|
|
|
2019-11-05 09:31:14 +01:00
|
|
|
free(argv);
|
2022-04-11 12:19:40 +02:00
|
|
|
free(envp);
|
2021-06-23 14:22:18 +02:00
|
|
|
tll_free_and_free(overrides, free);
|
2019-11-01 20:37:22 +01:00
|
|
|
fdm_del(fdm, fd);
|
|
|
|
|
client->fd = -1;
|
|
|
|
|
|
2021-07-31 19:08:51 +02:00
|
|
|
if (client->instance != NULL &&
|
|
|
|
|
!client->instance->terminal->shutdown.in_progress)
|
|
|
|
|
{
|
2021-03-12 20:46:55 -03:00
|
|
|
term_shutdown(client->instance->terminal);
|
2021-07-31 19:08:51 +02:00
|
|
|
} else
|
2019-11-01 20:37:22 +01:00
|
|
|
client_destroy(client);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
fdm_server(struct fdm *fdm, int fd, int events, void *data)
|
|
|
|
|
{
|
|
|
|
|
if (events & EPOLLHUP)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
struct server *server = data;
|
|
|
|
|
|
|
|
|
|
struct sockaddr_un addr;
|
|
|
|
|
socklen_t addr_size = sizeof(addr);
|
|
|
|
|
int client_fd = accept4(
|
2020-01-10 19:27:17 +01:00
|
|
|
server->fd, (struct sockaddr *)&addr, &addr_size, SOCK_CLOEXEC | SOCK_NONBLOCK);
|
2019-11-01 20:37:22 +01:00
|
|
|
|
|
|
|
|
if (client_fd == -1) {
|
|
|
|
|
LOG_ERRNO("failed to accept client connection");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-08 20:34:30 +01:00
|
|
|
struct client *client = xmalloc(sizeof(*client));
|
2019-11-01 20:37:22 +01:00
|
|
|
*client = (struct client) {
|
|
|
|
|
.server = server,
|
|
|
|
|
.fd = client_fd,
|
|
|
|
|
};
|
|
|
|
|
|
2020-01-10 19:49:26 +01:00
|
|
|
if (!fdm_add(server->fdm, client_fd, EPOLLIN, &fdm_client, client)) {
|
2019-11-01 20:37:22 +01:00
|
|
|
close(client_fd);
|
|
|
|
|
free(client);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_DBG("client FD=%d: connected", client_fd);
|
|
|
|
|
tll_push_back(server->clients, client);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum connect_status {CONNECT_ERR, CONNECT_FAIL, CONNECT_SUCCESS};
|
|
|
|
|
|
|
|
|
|
static enum connect_status
|
|
|
|
|
try_connect(const char *sock_path)
|
|
|
|
|
{
|
|
|
|
|
enum connect_status ret = CONNECT_ERR;
|
|
|
|
|
|
2019-11-03 01:48:40 +01:00
|
|
|
int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
|
2019-11-01 20:37:22 +01:00
|
|
|
if (fd == -1) {
|
|
|
|
|
LOG_ERRNO("failed to create UNIX socket");
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct sockaddr_un addr = {.sun_family = AF_UNIX};
|
|
|
|
|
strncpy(addr.sun_path, sock_path, sizeof(addr.sun_path) - 1);
|
|
|
|
|
|
2019-11-02 23:25:14 +01:00
|
|
|
switch (connect(fd, (struct sockaddr *)&addr, sizeof(addr))) {
|
|
|
|
|
case 0:
|
|
|
|
|
ret = CONNECT_SUCCESS;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case -1:
|
|
|
|
|
LOG_DBG("connect() failed: %s", strerror(errno));
|
|
|
|
|
ret = CONNECT_FAIL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2019-11-01 20:37:22 +01:00
|
|
|
|
|
|
|
|
err:
|
|
|
|
|
if (fd != -1)
|
|
|
|
|
close(fd);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-15 16:58:32 +01:00
|
|
|
static bool
|
|
|
|
|
prepare_socket(int fd)
|
|
|
|
|
{
|
|
|
|
|
int flags = fcntl(fd, F_GETFD);
|
|
|
|
|
if (flags < 0) {
|
|
|
|
|
LOG_ERRNO("failed to get file descriptors flag for passed socket");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) {
|
|
|
|
|
LOG_ERRNO("failed to set FD_CLOEXEC for passed socket");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
flags = fcntl(fd, F_GETFL);
|
|
|
|
|
if (flags < 0) {
|
|
|
|
|
LOG_ERRNO("failed to get file status flags for passed socket");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
|
|
|
|
|
LOG_ERRNO("failed to set non-blocking mode on passed socket");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int const socket_options[] = { SO_DOMAIN, SO_ACCEPTCONN, SO_TYPE };
|
2025-02-13 16:16:43 +01:00
|
|
|
int const socket_options_values[] = { AF_UNIX, NON_ZERO_OPT, SOCK_STREAM};
|
2022-01-15 16:58:32 +01:00
|
|
|
char const * const socket_options_names[] = { "SO_DOMAIN", "SO_ACCEPTCONN", "SO_TYPE" };
|
|
|
|
|
|
|
|
|
|
xassert(ALEN(socket_options) == ALEN(socket_options_values));
|
|
|
|
|
xassert(ALEN(socket_options) == ALEN(socket_options_names));
|
|
|
|
|
|
|
|
|
|
int socket_option = 0;
|
|
|
|
|
socklen_t len;
|
|
|
|
|
for (size_t i = 0; i < ALEN(socket_options) ; i++) {
|
|
|
|
|
len = sizeof(socket_option);
|
|
|
|
|
if (getsockopt(fd, SOL_SOCKET, socket_options[i], &socket_option, &len) == -1 ||
|
|
|
|
|
len != sizeof(socket_option)) {
|
|
|
|
|
LOG_ERRNO("failed to read socket option from passed file descriptor");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2025-02-13 16:16:43 +01:00
|
|
|
if (socket_options_values[i] == NON_ZERO_OPT && socket_option)
|
|
|
|
|
socket_option = NON_ZERO_OPT;
|
2022-01-15 16:58:32 +01:00
|
|
|
if (socket_option != socket_options_values[i]) {
|
|
|
|
|
LOG_ERR("wrong socket value for socket option '%s' on passed file descriptor",
|
|
|
|
|
socket_options_names[i]);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-01 20:37:22 +01:00
|
|
|
struct server *
|
2025-07-30 12:23:39 +02:00
|
|
|
server_init(struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
2020-05-21 20:17:29 +02:00
|
|
|
struct wayland *wayl)
|
2019-11-01 20:37:22 +01:00
|
|
|
{
|
2022-01-15 16:58:32 +01:00
|
|
|
int fd;
|
2019-11-01 20:37:22 +01:00
|
|
|
struct server *server = NULL;
|
2019-12-14 12:59:38 +01:00
|
|
|
const char *sock_path = conf->server_socket_path;
|
2022-01-15 16:58:32 +01:00
|
|
|
char *end;
|
2019-11-01 20:37:22 +01:00
|
|
|
|
2022-01-15 16:58:32 +01:00
|
|
|
errno = 0;
|
|
|
|
|
fd = strtol(sock_path, &end, 10);
|
|
|
|
|
if (*end == '\0' && *sock_path != '\0')
|
|
|
|
|
{
|
|
|
|
|
if (!prepare_socket(fd))
|
|
|
|
|
goto err;
|
|
|
|
|
LOG_DBG("we've been started by socket activation, using passed socket");
|
|
|
|
|
sock_path = NULL;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
LOG_DBG("no suitable pre-existing socket found, creating our own");
|
|
|
|
|
fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
|
|
|
|
|
if (fd == -1) {
|
|
|
|
|
LOG_ERRNO("failed to create UNIX socket");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2019-11-01 20:37:22 +01:00
|
|
|
|
2022-01-15 16:58:32 +01:00
|
|
|
switch (try_connect(sock_path)) {
|
|
|
|
|
case CONNECT_FAIL:
|
|
|
|
|
break;
|
2019-11-01 20:37:22 +01:00
|
|
|
|
2022-01-15 16:58:32 +01:00
|
|
|
case CONNECT_SUCCESS:
|
|
|
|
|
LOG_ERR("%s is already accepting connections; is 'foot --server' already running", sock_path);
|
|
|
|
|
/* FALLTHROUGH */
|
2019-11-01 20:37:22 +01:00
|
|
|
|
2022-01-15 16:58:32 +01:00
|
|
|
case CONNECT_ERR:
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
2019-11-01 20:37:22 +01:00
|
|
|
|
2022-01-15 16:58:32 +01:00
|
|
|
unlink(sock_path);
|
2019-11-01 20:37:22 +01:00
|
|
|
|
2022-01-15 16:58:32 +01:00
|
|
|
struct sockaddr_un addr = {.sun_family = AF_UNIX};
|
|
|
|
|
strncpy(addr.sun_path, sock_path, sizeof(addr.sun_path) - 1);
|
2019-11-01 20:37:22 +01:00
|
|
|
|
2022-01-15 16:58:32 +01:00
|
|
|
if (bind(fd, (const struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
|
|
|
|
LOG_ERRNO("%s: failed to bind", addr.sun_path);
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (listen(fd, 0) < 0) {
|
|
|
|
|
LOG_ERRNO("%s: failed to listen", addr.sun_path);
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
2019-11-01 20:37:22 +01:00
|
|
|
}
|
2019-11-02 23:25:14 +01:00
|
|
|
|
2019-11-01 20:37:22 +01:00
|
|
|
server = malloc(sizeof(*server));
|
2020-08-08 20:34:30 +01:00
|
|
|
if (unlikely(server == NULL)) {
|
|
|
|
|
LOG_ERRNO("malloc() failed");
|
|
|
|
|
goto err;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-01 20:37:22 +01:00
|
|
|
*server = (struct server) {
|
|
|
|
|
.conf = conf,
|
|
|
|
|
.fdm = fdm,
|
2020-05-21 20:17:29 +02:00
|
|
|
.reaper = reaper,
|
2019-11-01 20:37:22 +01:00
|
|
|
.wayl = wayl,
|
2019-11-02 23:25:14 +01:00
|
|
|
|
2019-11-01 20:37:22 +01:00
|
|
|
.fd = fd,
|
|
|
|
|
.sock_path = sock_path,
|
|
|
|
|
|
|
|
|
|
.clients = tll_init(),
|
2021-03-12 20:46:55 -03:00
|
|
|
.terminals = tll_init(),
|
2019-11-01 20:37:22 +01:00
|
|
|
};
|
|
|
|
|
|
2019-11-03 00:25:17 +01:00
|
|
|
if (!fdm_add(fdm, fd, EPOLLIN, &fdm_server, server))
|
2019-11-01 20:37:22 +01:00
|
|
|
goto err;
|
|
|
|
|
|
2022-01-15 16:58:32 +01:00
|
|
|
LOG_INFO("accepting connections on %s", sock_path != NULL ? sock_path : "socket provided through socket activation");
|
2019-12-13 22:28:48 +01:00
|
|
|
|
2019-11-01 20:37:22 +01:00
|
|
|
return server;
|
|
|
|
|
|
|
|
|
|
err:
|
|
|
|
|
free(server);
|
|
|
|
|
if (fd != -1)
|
|
|
|
|
close(fd);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
server_destroy(struct server *server)
|
|
|
|
|
{
|
|
|
|
|
if (server == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
LOG_DBG("server destroy, %zu clients still alive",
|
|
|
|
|
tll_length(server->clients));
|
|
|
|
|
|
|
|
|
|
tll_foreach(server->clients, it) {
|
2021-05-01 10:46:40 +02:00
|
|
|
client_send_exit_code(it->item, -26);
|
2019-11-01 20:37:22 +01:00
|
|
|
client_destroy(it->item);
|
|
|
|
|
}
|
2021-03-13 07:41:05 -03:00
|
|
|
|
2019-11-01 20:37:22 +01:00
|
|
|
tll_free(server->clients);
|
|
|
|
|
|
2021-03-13 07:41:05 -03:00
|
|
|
tll_foreach(server->terminals, it)
|
2021-03-12 20:46:55 -03:00
|
|
|
instance_destroy(it->item, 1);
|
2021-03-13 07:41:05 -03:00
|
|
|
|
2021-03-12 20:46:55 -03:00
|
|
|
tll_free(server->terminals);
|
|
|
|
|
|
2019-11-01 20:37:22 +01:00
|
|
|
fdm_del(server->fdm, server->fd);
|
2019-11-02 23:32:13 +01:00
|
|
|
if (server->sock_path != NULL)
|
|
|
|
|
unlink(server->sock_path);
|
2019-11-01 20:37:22 +01:00
|
|
|
free(server);
|
|
|
|
|
}
|
2025-07-30 12:23:39 +02:00
|
|
|
|
|
|
|
|
void
|
config: add [colors-dark] and [colors-light], replacing [colors] and [colors2]
The main reason for having two color sections is to be able to switch
between dark and light. Thus, it's better if the section names reflect
this, rather than the more generic 'colors' and 'colors2' (which was
the dark one and which was the light one, now again?)
When the second color section was added, we kept the original name,
colors, to make sure we didn't break existing configurations, and
third-party themes.
However, in the long run, it's probably better to be specific in the
section naming, to avoid confusion.
So, add 'colors-dark', and 'colors-light'. Keep 'colors' and 'colors2'
as aliases for now, but mark them as deprecated. They WILL be removed
in a future release.
Also rename the option values for initial-color-theme, from 1/2, to
dark/light. Keep the old ones for now, marked as deprecated.
Update all bundled themes to use the new names. In the light-only
themes (i.e. themes that define a single, light, theme), use
colors-light, and set initial-color-theme=light.
Possible improvements: disable color switching if only one color
section has been explicitly configured (todo: figure out how to handle
the default color theme values...)
2025-12-19 09:29:06 +01:00
|
|
|
server_global_theme_switch_to_dark(struct server *server)
|
2025-07-30 12:23:39 +02:00
|
|
|
{
|
config: add [colors-dark] and [colors-light], replacing [colors] and [colors2]
The main reason for having two color sections is to be able to switch
between dark and light. Thus, it's better if the section names reflect
this, rather than the more generic 'colors' and 'colors2' (which was
the dark one and which was the light one, now again?)
When the second color section was added, we kept the original name,
colors, to make sure we didn't break existing configurations, and
third-party themes.
However, in the long run, it's probably better to be specific in the
section naming, to avoid confusion.
So, add 'colors-dark', and 'colors-light'. Keep 'colors' and 'colors2'
as aliases for now, but mark them as deprecated. They WILL be removed
in a future release.
Also rename the option values for initial-color-theme, from 1/2, to
dark/light. Keep the old ones for now, marked as deprecated.
Update all bundled themes to use the new names. In the light-only
themes (i.e. themes that define a single, light, theme), use
colors-light, and set initial-color-theme=light.
Possible improvements: disable color switching if only one color
section has been explicitly configured (todo: figure out how to handle
the default color theme values...)
2025-12-19 09:29:06 +01:00
|
|
|
server->conf->initial_color_theme = COLOR_THEME_DARK;
|
2025-07-30 12:23:39 +02:00
|
|
|
tll_foreach(server->clients, it)
|
config: add [colors-dark] and [colors-light], replacing [colors] and [colors2]
The main reason for having two color sections is to be able to switch
between dark and light. Thus, it's better if the section names reflect
this, rather than the more generic 'colors' and 'colors2' (which was
the dark one and which was the light one, now again?)
When the second color section was added, we kept the original name,
colors, to make sure we didn't break existing configurations, and
third-party themes.
However, in the long run, it's probably better to be specific in the
section naming, to avoid confusion.
So, add 'colors-dark', and 'colors-light'. Keep 'colors' and 'colors2'
as aliases for now, but mark them as deprecated. They WILL be removed
in a future release.
Also rename the option values for initial-color-theme, from 1/2, to
dark/light. Keep the old ones for now, marked as deprecated.
Update all bundled themes to use the new names. In the light-only
themes (i.e. themes that define a single, light, theme), use
colors-light, and set initial-color-theme=light.
Possible improvements: disable color switching if only one color
section has been explicitly configured (todo: figure out how to handle
the default color theme values...)
2025-12-19 09:29:06 +01:00
|
|
|
term_theme_switch_to_dark(it->item->instance->terminal);
|
2025-07-30 12:23:39 +02:00
|
|
|
tll_foreach(server->terminals, it)
|
config: add [colors-dark] and [colors-light], replacing [colors] and [colors2]
The main reason for having two color sections is to be able to switch
between dark and light. Thus, it's better if the section names reflect
this, rather than the more generic 'colors' and 'colors2' (which was
the dark one and which was the light one, now again?)
When the second color section was added, we kept the original name,
colors, to make sure we didn't break existing configurations, and
third-party themes.
However, in the long run, it's probably better to be specific in the
section naming, to avoid confusion.
So, add 'colors-dark', and 'colors-light'. Keep 'colors' and 'colors2'
as aliases for now, but mark them as deprecated. They WILL be removed
in a future release.
Also rename the option values for initial-color-theme, from 1/2, to
dark/light. Keep the old ones for now, marked as deprecated.
Update all bundled themes to use the new names. In the light-only
themes (i.e. themes that define a single, light, theme), use
colors-light, and set initial-color-theme=light.
Possible improvements: disable color switching if only one color
section has been explicitly configured (todo: figure out how to handle
the default color theme values...)
2025-12-19 09:29:06 +01:00
|
|
|
term_theme_switch_to_dark(it->item->terminal);
|
2025-07-30 12:23:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
config: add [colors-dark] and [colors-light], replacing [colors] and [colors2]
The main reason for having two color sections is to be able to switch
between dark and light. Thus, it's better if the section names reflect
this, rather than the more generic 'colors' and 'colors2' (which was
the dark one and which was the light one, now again?)
When the second color section was added, we kept the original name,
colors, to make sure we didn't break existing configurations, and
third-party themes.
However, in the long run, it's probably better to be specific in the
section naming, to avoid confusion.
So, add 'colors-dark', and 'colors-light'. Keep 'colors' and 'colors2'
as aliases for now, but mark them as deprecated. They WILL be removed
in a future release.
Also rename the option values for initial-color-theme, from 1/2, to
dark/light. Keep the old ones for now, marked as deprecated.
Update all bundled themes to use the new names. In the light-only
themes (i.e. themes that define a single, light, theme), use
colors-light, and set initial-color-theme=light.
Possible improvements: disable color switching if only one color
section has been explicitly configured (todo: figure out how to handle
the default color theme values...)
2025-12-19 09:29:06 +01:00
|
|
|
server_global_theme_switch_to_light(struct server *server)
|
2025-07-30 12:23:39 +02:00
|
|
|
{
|
config: add [colors-dark] and [colors-light], replacing [colors] and [colors2]
The main reason for having two color sections is to be able to switch
between dark and light. Thus, it's better if the section names reflect
this, rather than the more generic 'colors' and 'colors2' (which was
the dark one and which was the light one, now again?)
When the second color section was added, we kept the original name,
colors, to make sure we didn't break existing configurations, and
third-party themes.
However, in the long run, it's probably better to be specific in the
section naming, to avoid confusion.
So, add 'colors-dark', and 'colors-light'. Keep 'colors' and 'colors2'
as aliases for now, but mark them as deprecated. They WILL be removed
in a future release.
Also rename the option values for initial-color-theme, from 1/2, to
dark/light. Keep the old ones for now, marked as deprecated.
Update all bundled themes to use the new names. In the light-only
themes (i.e. themes that define a single, light, theme), use
colors-light, and set initial-color-theme=light.
Possible improvements: disable color switching if only one color
section has been explicitly configured (todo: figure out how to handle
the default color theme values...)
2025-12-19 09:29:06 +01:00
|
|
|
server->conf->initial_color_theme = COLOR_THEME_LIGHT;
|
2025-07-30 12:23:39 +02:00
|
|
|
tll_foreach(server->clients, it)
|
config: add [colors-dark] and [colors-light], replacing [colors] and [colors2]
The main reason for having two color sections is to be able to switch
between dark and light. Thus, it's better if the section names reflect
this, rather than the more generic 'colors' and 'colors2' (which was
the dark one and which was the light one, now again?)
When the second color section was added, we kept the original name,
colors, to make sure we didn't break existing configurations, and
third-party themes.
However, in the long run, it's probably better to be specific in the
section naming, to avoid confusion.
So, add 'colors-dark', and 'colors-light'. Keep 'colors' and 'colors2'
as aliases for now, but mark them as deprecated. They WILL be removed
in a future release.
Also rename the option values for initial-color-theme, from 1/2, to
dark/light. Keep the old ones for now, marked as deprecated.
Update all bundled themes to use the new names. In the light-only
themes (i.e. themes that define a single, light, theme), use
colors-light, and set initial-color-theme=light.
Possible improvements: disable color switching if only one color
section has been explicitly configured (todo: figure out how to handle
the default color theme values...)
2025-12-19 09:29:06 +01:00
|
|
|
term_theme_switch_to_light(it->item->instance->terminal);
|
2025-07-30 12:23:39 +02:00
|
|
|
tll_foreach(server->terminals, it)
|
config: add [colors-dark] and [colors-light], replacing [colors] and [colors2]
The main reason for having two color sections is to be able to switch
between dark and light. Thus, it's better if the section names reflect
this, rather than the more generic 'colors' and 'colors2' (which was
the dark one and which was the light one, now again?)
When the second color section was added, we kept the original name,
colors, to make sure we didn't break existing configurations, and
third-party themes.
However, in the long run, it's probably better to be specific in the
section naming, to avoid confusion.
So, add 'colors-dark', and 'colors-light'. Keep 'colors' and 'colors2'
as aliases for now, but mark them as deprecated. They WILL be removed
in a future release.
Also rename the option values for initial-color-theme, from 1/2, to
dark/light. Keep the old ones for now, marked as deprecated.
Update all bundled themes to use the new names. In the light-only
themes (i.e. themes that define a single, light, theme), use
colors-light, and set initial-color-theme=light.
Possible improvements: disable color switching if only one color
section has been explicitly configured (todo: figure out how to handle
the default color theme values...)
2025-12-19 09:29:06 +01:00
|
|
|
term_theme_switch_to_light(it->item->terminal);
|
2025-07-30 12:23:39 +02:00
|
|
|
}
|