447 lines
13 KiB
D
447 lines
13 KiB
D
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
|
|
}
|
|
} |