dst/source/main.d

447 lines
13 KiB
D
Raw Permalink Normal View History

2025-06-26 18:47:07 +00:00
module main;
import core.stdc.stdio;
import core.stdc.stdlib;
import core.stdc.locale;
import core.stdc.signal;
import core.stdc.string;
import core.stdc.stdlib : malloc;
import std.string : toStringz;
import std.conv : to;
import st;
import x;
import config;
import patches;
import arg;
// Import X11 Display type
import deimos.X11.Xlib : Display;
// Xresources types
alias XrmDatabase = void*;
struct XrmValue {
uint size;
char* addr;
}
enum ResourceType {
STRING = 0,
INTEGER = 1,
FLOAT = 2
}
struct ResourcePref {
const(char)* name;
ResourceType type;
void* dst;
}
enum VERSION = "0.9.2";
// Global variables
__gshared {
string opt_alpha;
string opt_class = "St";
string opt_dir;
string opt_font = "";
string opt_io;
string opt_line;
string opt_name = "st";
string opt_title;
string opt_embed;
string[] opt_cmd;
int cols = 80;
int rows = 24;
string usedfont;
}
void usage() {
string msg = "usage: " ~ argv0 ~ " [-aiv] [-c class]";
static if (isPatchEnabled!"WORKINGDIR_PATCH") {
msg ~= " [-d path]";
}
msg ~= " [-f font] [-g geometry] [-n name] [-o iofile] [-T title] [-t title] [-w windowid]";
static if (isPatchEnabled!"ALPHA_PATCH") {
msg ~= " [-A alpha]";
}
msg ~= " [[-e] command [args ...]]\n";
msg ~= " " ~ argv0 ~ " [-aiv] [-c class]";
static if (isPatchEnabled!"WORKINGDIR_PATCH") {
msg ~= " [-d path]";
}
msg ~= " [-f font] [-g geometry] [-n name] [-o iofile] [-T title] [-t title] [-w windowid]";
static if (isPatchEnabled!"ALPHA_PATCH") {
msg ~= " [-A alpha]";
}
msg ~= " -l line [stty_args ...]\n";
die(msg.toStringz);
}
void handleArg(char opt, string delegate() getArg, string delegate(lazy void) requireArg) {
switch (opt) {
case 'a':
config.allowaltscreen = 0;
break;
static if (isPatchEnabled!"ALPHA_PATCH") {
case 'A':
opt_alpha = requireArg(usage());
break;
}
case 'c':
opt_class = requireArg(usage());
break;
static if (isPatchEnabled!"WORKINGDIR_PATCH") {
case 'd':
opt_dir = requireArg(usage());
break;
}
case 'f':
opt_font = requireArg(usage());
break;
case 'g':
auto geom = requireArg(usage());
parseGeometry(geom);
break;
case 'i':
xw.isfixed = 1;
break;
case 'o':
opt_io = requireArg(usage());
break;
case 'l':
opt_line = requireArg(usage());
break;
case 'n':
opt_name = requireArg(usage());
break;
case 't':
case 'T':
opt_title = requireArg(usage());
break;
case 'w':
opt_embed = requireArg(usage());
break;
case 'v':
die("%s %s\n", argv0.toStringz, VERSION.toStringz);
break;
default:
usage();
}
}
void parseGeometry(string geom) {
import std.regex;
import std.conv;
// Parse X11 geometry string: WIDTHxHEIGHT+X+Y
auto re = regex(r"^(\d+)x(\d+)(?:\+(-?\d+)\+(-?\d+))?$");
auto m = geom.matchFirst(re);
if (!m.empty) {
if (m[1].length) cols = m[1].to!int;
if (m[2].length) rows = m[2].to!int;
if (m[3].length) xw.l = m[3].to!int;
if (m[4].length) xw.t = m[4].to!int;
xw.gm = 1;
}
}
extern(C) void sigusr1_reload(int) {
static if ((isPatchEnabled!"XRESOURCES_PATCH" && isPatchEnabled!"XRESOURCES_RELOAD_PATCH") ||
(isPatchEnabled!"BACKGROUND_IMAGE_PATCH" && isPatchEnabled!"BACKGROUND_IMAGE_RELOAD_PATCH")) {
// Signal handler for reloading configuration
static if (isPatchEnabled!"XRESOURCES_PATCH" && isPatchEnabled!"XRESOURCES_RELOAD_PATCH") {
config_init(xw.dpy);
}
static if (isPatchEnabled!"BACKGROUND_IMAGE_PATCH" && isPatchEnabled!"BACKGROUND_IMAGE_RELOAD_PATCH") {
import patch.background_image : reload_image;
reload_image();
}
redraw();
}
}
extern(C) int main(int argc, char** argv) {
import std.logger : globalLogLevel, LogLevel, FileLogger, sharedLog, trace;
import std.stdio : stderr;
// Set global log level to trace for debugging
globalLogLevel = LogLevel.trace;
// Create a FileLogger that outputs to stderr with trace level
auto stderrLogger = new FileLogger(stderr, LogLevel.trace);
// Set it as the shared logger
sharedLog = cast(shared)stderrLogger;
// Log application startup
trace("ST terminal starting up...");
// Initialize shell pointer
config.shell_ptr = config._shell_array.ptr;
// Parse command line arguments
string[] args;
args.length = argc;
foreach (i; 0..argc) {
args[i] = to!string(argv[i]);
}
// Handle -e option specially in handleArg
bool hasEOption = false;
parseArgs(args, (char opt, string delegate() getArg, string delegate(lazy void) requireArg) {
if (opt == 'e') {
hasEOption = true;
// Skip the -e handling, parseArgs will leave remaining args in args array
return;
}
handleArg(opt, getArg, requireArg);
});
// After parseArgs, args contains only unparsed arguments
if (hasEOption || args.length > 0) {
opt_cmd = args;
}
// Set default title if not specified
if (!opt_title) {
opt_title = (opt_line || opt_cmd.length == 0) ? "st" : opt_cmd[0];
}
// Set locale
setlocale(LC_CTYPE, "");
// Set locale modifiers for X11
setXLocaleModifiers();
// Setup signal handlers
static if ((isPatchEnabled!"XRESOURCES_PATCH" && isPatchEnabled!"XRESOURCES_RELOAD_PATCH") ||
(isPatchEnabled!"BACKGROUND_IMAGE_PATCH" && isPatchEnabled!"BACKGROUND_IMAGE_RELOAD_PATCH")) {
signal(SIGUSR1, &sigusr1_reload);
}
// Open X display for xresources
static if (isPatchEnabled!"XRESOURCES_PATCH") {
import deimos.X11.Xlib : XOpenDisplay;
xw.dpy = XOpenDisplay(null);
if (!xw.dpy) {
die("Can't open display\n");
}
config_init(xw.dpy);
}
// Initialize harfbuzz if needed
static if (isPatchEnabled!"LIGATURES_PATCH") {
hbcreatebuffer();
}
// Ensure minimum size
cols = max(cols, 1);
rows = max(rows, 1);
// Set default background for alpha focus
static if (isPatchEnabled!"ALPHA_PATCH" && isPatchEnabled!"ALPHA_FOCUS_HIGHLIGHT_PATCH") {
import std.algorithm : max;
config.defaultbg = max(colorname.length, 256);
}
// Initialize terminal
tnew(cols, rows);
xinit(cols, rows);
// Initialize window position and fixed state
xw.l = xw.t = 0;
xw.isfixed = 0;
// Set cursor style
static if (isPatchEnabled!"BLINKING_CURSOR_PATCH") {
xsetcursor(cursorstyle);
} else {
xsetcursor(cursorshape);
}
// Start terminal process
if (ttynew(opt_line ? opt_line.toStringz : null,
config.shell_ptr ? config.shell_ptr[0] : null, // shell
opt_io ? opt_io.toStringz : null,
opt_cmd.length > 0 ? toCStringArray(opt_cmd) : null) < 0) {
die("Failed to start terminal process\n");
}
// Force initial draw
redraw();
// Initialize background image if enabled
static if (isPatchEnabled!"BACKGROUND_IMAGE_PATCH") {
bginit();
}
// Set environment variables
xsetenv();
// Initialize selection
selinit();
// Change working directory if specified
static if (isPatchEnabled!"WORKINGDIR_PATCH") {
if (opt_dir) {
import core.sys.posix.unistd : chdir;
if (chdir(opt_dir.toStringz) != 0) {
die("Can't change to working directory %s\n", opt_dir.toStringz);
}
}
}
// Run the main event loop
run();
// Cleanup
static if (isPatchEnabled!"LIGATURES_PATCH") {
hbdestroybuffer();
}
return 0;
}
// External X11 function
extern(C) char* XSetLocaleModifiers(const(char)*);
// Set X locale modifiers
void setXLocaleModifiers() {
XSetLocaleModifiers("".toStringz);
}
// External declarations needed
extern(C) {
void xsetcursor(int);
void xinit(int, int);
void xsetenv();
void run();
void bginit();
void hbcreatebuffer();
void hbdestroybuffer();
void redraw();
int ttynew(const(char)* line, char* cmd, const(char)* outfile, char** args);
// Xresources functions
void XrmInitialize();
char* XResourceManagerString(Display* dpy);
XrmDatabase XrmGetStringDatabase(const(char)* data);
int XrmGetResource(XrmDatabase db, const(char)* str_name, const(char)* str_class,
char** str_type_return, XrmValue* value_return);
// Additional C library functions
uint strtoul(const(char)* nptr, char** endptr, int base);
float strtof(const(char)* nptr, char** endptr);
__gshared {
extern int cursorstyle;
extern const(char)*[] colorname;
}
}
// Helper function to convert D string array to C string array
char** toCStringArray(string[] arr) {
if (arr.length == 0) return null;
char** result = cast(char**)malloc((arr.length + 1) * (char*).sizeof);
foreach (i, s; arr) {
result[i] = cast(char*)s.toStringz;
}
result[arr.length] = null;
return result;
}
// Xresources implementation
static if (isPatchEnabled!"XRESOURCES_PATCH") {
ResourcePref[] get_resources() {
ResourcePref[] resources;
resources ~= ResourcePref("font", ResourceType.STRING, cast(void*)&config.font);
// Add color resources
import config : colorname;
for (int i = 0; i < 16; i++) {
static char[16][16] color_names;
snprintf(color_names[i].ptr, 16, "color%d", i);
resources ~= ResourcePref(color_names[i].ptr, ResourceType.STRING, cast(void*)&colorname[i]);
}
resources ~= ResourcePref("background", ResourceType.STRING, cast(void*)&colorname[258]);
resources ~= ResourcePref("foreground", ResourceType.STRING, cast(void*)&colorname[259]);
resources ~= ResourcePref("cursorColor", ResourceType.STRING, cast(void*)&colorname[256]);
resources ~= ResourcePref("termname", ResourceType.STRING, cast(void*)&config.termname);
resources ~= ResourcePref("shell", ResourceType.STRING, cast(void*)&config.shell);
resources ~= ResourcePref("minlatency", ResourceType.INTEGER, cast(void*)&config.minlatency);
resources ~= ResourcePref("maxlatency", ResourceType.INTEGER, cast(void*)&config.maxlatency);
resources ~= ResourcePref("blinktimeout", ResourceType.INTEGER, cast(void*)&config.blinktimeout);
resources ~= ResourcePref("bellvolume", ResourceType.INTEGER, cast(void*)&config.bellvolume);
resources ~= ResourcePref("tabspaces", ResourceType.INTEGER, cast(void*)&config.tabspaces);
resources ~= ResourcePref("borderpx", ResourceType.INTEGER, cast(void*)&config.borderpx);
return resources;
}
int resource_load(XrmDatabase db, const(char)* name, ResourceType rtype, void* dst) {
import std.string : fromStringz;
char[256] fullname;
char[256] fullclass;
char* type;
XrmValue ret;
const(char)* opt_name_str = (opt_name.length > 0 ? opt_name : "st").toStringz;
const(char)* opt_class_str = (opt_class.length > 0 ? opt_class : "St").toStringz;
snprintf(fullname.ptr, fullname.sizeof, "%s.%s", opt_name_str, name);
snprintf(fullclass.ptr, fullclass.sizeof, "%s.%s", opt_class_str, name);
fullname[$-1] = fullclass[$-1] = '\0';
if (!XrmGetResource(db, fullname.ptr, fullclass.ptr, &type, &ret))
return 1;
if (ret.addr is null || strncmp("String", type, 64) != 0)
return 1;
switch (rtype) {
case ResourceType.STRING:
*cast(char**)dst = ret.addr;
break;
case ResourceType.INTEGER:
*cast(int*)dst = cast(int)strtoul(ret.addr, null, 10);
break;
case ResourceType.FLOAT:
*cast(float*)dst = strtof(ret.addr, null);
break;
default:
return 1;
}
return 0;
}
extern(C) void config_init(Display* dpy) {
char* resm;
XrmDatabase db;
XrmInitialize();
resm = XResourceManagerString(dpy);
if (!resm) {
return;
}
db = XrmGetStringDatabase(resm);
auto resources = get_resources();
foreach (ref resource; resources) {
resource_load(db, resource.name, resource.type, resource.dst);
}
}
} else {
extern(C) void config_init(Display* dpy) {
// No-op when xresources is disabled
}
}