env.Program("bin/#{bin}", [src, "libtide.a"])
end
+ (glob("src/*/") - ["src/lib"]).each do |dir|
+ bin = File.basename(dir)
+ env.Program("bin/#{bin}", glob("#{dir}/**/*.c") + ["libtide.a"])
+ end
+
+
# Generate the documentation
env.Command("", "tools/docgen",
"CMD" => ["${_SOURCES}"],
+++ /dev/null
-= edit(1)
-:doctype: manpage
-
-== NAME
-
-registrar - registry service for tracking open tide(1) windows
-
-== SYNOPSIS
-
-*registrar*
-
-== DESCRIPTION
-
-
-== OPTIONS
-
-
-== ENVIRONMENT
-
-
-== SEE ALSO
-
-registrar(1), tide(1), pick(1), fetch(1), fetchsel(1)
+++ /dev/null
-#include <stdc.h>
-#include <x11.h>
-#include <io.h>
-
-#include "config.h"
-
-char* ARGV0;
-Atom XA_REGISTRAR, XA_OPEN, XA_DONE;
-
-int spawn(char* cmd)
-{
- int pid = fork();
- if (pid == 0)
- {
- exit(execvp(cmd, (char*[]){cmd, 0}));
- }
- return pid;
-}
-
-Window start_registrar(XConf* x)
-{
- /* launch registrar if it isn't yet running */
- if (None == XGetSelectionOwner(x->display, XA_REGISTRAR))
- {
- if (spawn("registrar") > 0)
- {
- sleep(1);
- }
- }
- return XGetSelectionOwner(x->display, XA_REGISTRAR);
-}
-
-void prop_set(XConf* x, Window win, char* prop, char* value)
-{
- Atom xa_prop = XInternAtom(x->display, prop, False);
- XChangeProperty(
- x->display, win, xa_prop, XA_STRING, 8, PropModeReplace,
- (const unsigned char *)value, strlen(value)+1);
-}
-
-void edit_file(XConf* x, Window registrar, char* path, char* addr, int force)
-{
- char host[8192];
- path = abspath(path);
- prop_set(x, x->self, "FILE", path);
- prop_set(x, x->self, "ADDR", addr);
- if (!gethostname(host, sizeof(host)))
- {
- prop_set(x, x->self, "HOST", host);
- }
- XChangeProperty(
- x->display, registrar, XA_OPEN, XA_WINDOW, 32, PropModeAppend,
- (const unsigned char *)&(x->self), 1);
- EditCmd[2] = addr, EditCmd[3] = path;
- if (force)
- {
- if (!fork())
- exit(execvp(EditCmd[0], EditCmd));
- }
- else
- {
- /* wait for the "done" message */
- for (XEvent e;;)
- {
- XNextEvent(x->display, &e);
- if (e.type == ClientMessage)
- {
- if (e.xclient.message_type == XA_DONE)
- {
- break;
- }
- else if (e.xclient.message_type == XA_OPEN)
- {
- if (!fork())
- {
- exit(execvp(EditCmd[0], EditCmd));
- }
- break;
- }
- }
- }
- }
- free(path);
-}
-
-int main(int argc, char** argv)
-{
- int ret = 0;
- int force = 0;
- OPTBEGIN { case 'f': force = 1; break; } OPTEND;
- if (argc == 0)
- {
- spawn("tide");
- }
- else
- {
- XConf x = {0};
- x11_init(&x);
- x11_mkwin(&x, 1, 1, 0);
- XA_REGISTRAR = XInternAtom(x.display, "TIDE_REGISTRAR", PropertyChangeMask);
- XA_OPEN = XInternAtom(x.display, "OPEN", 0);
- XA_DONE = XInternAtom(x.display, "DONE", 0);
- Window registrar = start_registrar(&x);
- if (registrar != None)
- {
- /* Loop over files and send an OPEN message for each one. */
- for (int i = 0; i < argc; i++)
- {
- char* addr = strrchr(argv[i], ':');
- if (addr)
- {
- *addr = '\0', addr++;
- }
- edit_file(&x, registrar, argv[i], (addr ? addr : "0"), force);
- }
- XSync(x.display, False);
- }
- else
- {
- fprintf(stderr, "Failed to contact registrar.\n");
- ret = 1;
- }
- }
- return ret;
-}
+++ /dev/null
-= fetch(1)
-:doctype: manpage
-
-== NAME
-
-registrar - registry service for tracking open tide(1) windows
-
-== SYNOPSIS
-
-*registrar*
-
-== DESCRIPTION
-
-
-== OPTIONS
-
-
-== ENVIRONMENT
-
-
-== SEE ALSO
-
-registrar(1), tide(1), edit(1), pick(1), fetchsel(1)
\ No newline at end of file
+++ /dev/null
-#include <stdc.h>
-#include <io.h>
-#include <regex.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <pwd.h>
-
-typedef struct {
- enum {
- COMPLETE=0, MATCHES, IS, ISSET, ISDIR, ISFILE,
- SET, UNSET, EXEC, LAUNCH
- } type;
- char* arg1;
- char* arg2;
-} Rule;
-
-char* Matches[10];
-Rule*** Rulesets = NULL;
-size_t NumRulesets = 0;
-bool Launched = false;
-
-static Rule*** BuiltinRules = (Rule**[]){
- (Rule*[]){ /* Match URLS and open them with the browser */
- &(Rule){ ISSET, "BROWSER", NULL },
- &(Rule){ MATCHES, "data", "^(https?|ftp)://.*" },
- &(Rule){ LAUNCH, "$BROWSER \"$M0\"", NULL },
- &(Rule){ COMPLETE, NULL, NULL }
- },
- (Rule*[]){ /* Open files with addresses in the editor */
- &(Rule){ MATCHES, "data", "^([^:]+):([0-9]+)" },
- &(Rule){ ISFILE, "$M1", NULL },
- &(Rule){ LAUNCH, "edit \"$M0\"", NULL },
- &(Rule){ COMPLETE, NULL, NULL }
- },
- (Rule*[]){ /* Open addresses in the current file */
- &(Rule){ MATCHES, "data", "^:?([0-9]+)" },
- &(Rule){ ISFILE, "$file", NULL },
- &(Rule){ LAUNCH, "edit \"$file:$M1\"", NULL },
- &(Rule){ COMPLETE, NULL, NULL }
- },
- (Rule*[]){ /* If it's an existing text file, open it with editor */
- &(Rule){ ISFILE, "$data", NULL },
- &(Rule){ EXEC, "file --mime \"$file\" | grep -q 'text/'", NULL },
- &(Rule){ LAUNCH, "edit \"$file\"", NULL },
- &(Rule){ COMPLETE, NULL, NULL }
- },
- (Rule*[]){ /* Look it up in ctags database */
- &(Rule){ ISFILE, "tags", NULL },
- &(Rule){ EXEC, "grep -q \"^$data\\s\\+\" tags", NULL },
- &(Rule){ LAUNCH, "picktag fetch tags \"$data\"", NULL },
- &(Rule){ COMPLETE, NULL, NULL }
- },
- (Rule*[]){ /* Open man pages in a tide window */
- &(Rule){ ISSET, "BROWSER", NULL },
- &(Rule){ MATCHES, "data", "(.+)\\(([0-9]+)\\)" },
- &(Rule){ LAUNCH, "man $M2 \"$M1\" | col -b | tide -", NULL },
- &(Rule){ COMPLETE, NULL, NULL }
- },
- (Rule*[]){ /* If it's an existing directory, open it tide */
- &(Rule){ ISDIR, "$data", NULL },
- &(Rule){ LAUNCH, "edit \"$data\"", NULL },
- &(Rule){ COMPLETE, NULL, NULL }
- },
- (Rule*[]){ /* look for files in /usr/include */
- &(Rule){ ISFILE, "/usr/include/$data", NULL },
- &(Rule){ LAUNCH, "edit \"/usr/include/$data\"", NULL },
- &(Rule){ COMPLETE, NULL, NULL }
- },
- (Rule*[]){ /* look for files in /usr/local/include */
- &(Rule){ ISFILE, "/usr/local/include/$data", NULL },
- &(Rule){ LAUNCH, "edit \"/usr/local/include/$data\"", NULL },
- &(Rule){ COMPLETE, NULL, NULL }
- },
- (Rule*[]){ /* look for files in /usr/X11/include */
- &(Rule){ ISFILE, "/usr/X11/include/$data", NULL },
- &(Rule){ LAUNCH, "edit \"/usr/X11/include/$data\"", NULL },
- &(Rule){ COMPLETE, NULL, NULL }
- },
- NULL
-};
-
-static char* homedir_path(char* rpath)
-{
- static char path[8192] = {0};
- if (!path[0])
- {
- snprintf(path, sizeof(path)-1,"%s/%s", getpwuid(getuid())->pw_dir, rpath);
- }
- return path;
-}
-
-
-/******************************************************************************/
-
-char* getvar(char* val)
-{
- val = getenv(val);
- return (val ? val : "");
-}
-
-char* eval(char* str)
-{
- static bool inited = false;
- static char* patt = "\\$([a-zA-Z0-9_]+)";
- static regex_t regex;
-
- if (!inited && (regcomp(®ex, patt, REG_EXTENDED) < 0))
- {
- perror("regcomp() :");
- exit(1);
- }
-
- regmatch_t matches[2] = {{0},{0}};
- int result = regexec(®ex, str, nelem(matches), matches, 0);
- if (result >= 0 && matches[1].rm_so > 0)
- {
- char* var = strndup(str+matches[1].rm_so, matches[1].rm_eo-matches[1].rm_so);
- char* val = getvar(var);
- size_t sz = strlen(str) + strlen(val);
- char* exp = calloc(1, sz);
- strncat(exp, str, matches[0].rm_so);
- strcat(exp, val);
- strcat(exp, str + matches[0].rm_eo);
- str = eval(exp);
- }
- return str;
-}
-
-/******************************************************************************/
-
-static bool matches(char* var, char* patt)
-{
- bool ret = false;
- regex_t regex = {0};
- regmatch_t matches[10] = {{0},{0},{0},{0},{0},{0},{0},{0},{0},{0}};
- if (regcomp(®ex, patt, REG_EXTENDED) == 0)
- {
- var = getvar(var);
- int err = regexec(®ex, var, nelem(matches), matches, 0);
- for (int i = 0; i < 10 && matches[i].rm_so >= 0; i++)
- {
- char* matchval = strndup(var+matches[i].rm_so, matches[i].rm_eo-matches[i].rm_so);
- setenv((char[]){ 'M', ('0' + i), 0 }, matchval, 1);
- free(matchval);
- }
- ret = (err == 0);
- }
- return ret;
-}
-
-static bool var_is(char* var, char* val)
-{
- return (strcmp(getvar(var), eval(val)) == 0);
-}
-
-static bool var_isset(char* var)
-{
- return (getenv(var) != NULL);
-}
-
-static bool var_isdir(char* var)
-{
- bool ret = false;
- struct stat st = {0};
- char* path = eval(var);
- bool exists = (stat(eval(var), &st) >= 0);
- if (exists && S_ISDIR(st.st_mode))
- {
- setenv("dir", path, 1);
- ret = true;
- }
- return ret;
-}
-
-static bool var_isfile(char* var)
-{
- bool ret = false;
- struct stat st = {0};
- char* path = eval(var);
- bool exists = (stat(eval(var), &st) >= 0);
- if (exists && !S_ISDIR(st.st_mode))
- {
- setenv("file", path, 1);
- ret = true;
- }
- return ret;
-}
-
-static bool var_set(char* var, char* val)
-{
- return (setenv(var, eval(val), 1) == 0);
-}
-
-static bool var_unset(char* var)
-{
- return (unsetenv(var) == 0);
-}
-
-static void runcmd(char* cmd)
-{
- char* shellcmd[] = { getvar("SHELL"), "-c", cmd, NULL };
- if (!shellcmd[0]) shellcmd[0] = "/bin/sh";
- _exit(execvp(shellcmd[0], shellcmd));
-}
-
-static bool exec(char* cmd)
-{
- bool ret = false;
- int pid, status;
- if ((pid = fork()) >= 0)
- {
- if (pid == 0)
- {
- runcmd(cmd);
- }
- else
- {
- waitpid(pid, &status, 0);
- ret = (status == 0);
- }
- }
- return ret;
-}
-
-static bool launch(char* cmd)
-{
- bool ret = false;
- int pid = fork();
- if (pid > 0)
- {
- Launched = true;
- ret = true;
- }
- else if (pid == 0)
- {
- runcmd(cmd);
- }
- return ret;
-}
-
-static bool apply_rule(Rule* rule)
-{
- bool ret = false;
- switch (rule->type)
- {
- case COMPLETE: exit(0); break;
- case MATCHES: ret = matches(rule->arg1, rule->arg2); break;
- case IS: ret = var_is(rule->arg1, rule->arg2); break;
- case ISSET: ret = var_isset(rule->arg1); break;
- case ISDIR: ret = var_isdir(rule->arg1); break;
- case ISFILE: ret = var_isfile(rule->arg1); break;
- case SET: ret = var_set(rule->arg1, rule->arg2); break;
- case UNSET: ret = var_unset(rule->arg1); break;
- case EXEC: ret = exec(rule->arg1); break;
- case LAUNCH: ret = launch(rule->arg1); break;
- }
- return ret;
-}
-
-/******************************************************************************/
-
-static void usage(char* pname)
-{
- fprintf(stderr, "Usage: %s [ITEM]\n", pname);
- exit(1);
-}
-
-static char* type2str(int type)
-{
- char* ret = "UNKNOWN";
- switch (type)
- {
- case COMPLETE: ret = "COMPLETE"; break;
- case MATCHES: ret = "MATCHES"; break;
- case IS: ret = "IS"; break;
- case ISSET: ret = "ISSET"; break;
- case ISDIR: ret = "ISDIR"; break;
- case ISFILE: ret = "ISFILE"; break;
- case SET: ret = "SET"; break;
- case UNSET: ret = "UNSET"; break;
- case EXEC: ret = "EXEC"; break;
- case LAUNCH: ret = "LAUNCH"; break;
- }
- return ret;
-}
-
-static char* nextline(char** raw)
-{
- char* line = NULL;
- char* eol = strchr(*raw, '\n');
- if (eol)
- {
- *eol = '\0';
- line = *raw;
- *raw = eol+1;
- }
- if (line)
- {
- line = strdup(line);
- }
- return line;
-}
-
-static char* nextfield(char** raw)
-{
- char* field = *raw;
- /* skip whitespace */
- for (; *field && isspace(*field); field++);
-
- char* eof = field;
- for (; *eof && !isspace(*eof); eof++);
- *eof = '\0';
- *raw = eof + 1;
-
- return (field && *field ? field : NULL);
-}
-
-static char* lastfield(char** raw)
-{
- char* field = *raw;
- /* skip whitespace */
- for (; *field && isspace(*field); field++);
-
- char* eof = field;
- for (; *eof && *eof != '\n'; eof++);
- *eof = '\0';
- *raw = eof + 1;
-
- return (field && *field ? field : NULL);
-}
-
-
-static void parse_args1(int type, Rule* rule, char* args)
-{
- rule->type = type;
- rule->arg1 = args;
- if (!rule->arg1)
- {
- printf("action '%s' requires an argument\n", type2str(type));
- exit(1);
- }
-}
-
-static void parse_args2(int type, Rule* rule, char* args)
-{
- rule->type = type;
- rule->arg1 = nextfield(&args);
- rule->arg2 = lastfield(&args);
- if (!rule->arg1 || !rule->arg2)
- {
- printf("action '%s' requires two arguments\n", type2str(type));
- exit(1);
- }
-}
-
-static Rule* parse_rule(char* act, char* args)
-{
- Rule* rule = calloc(1, sizeof(Rule));
- if (!strcmp(act, "is"))
- {
- parse_args2(IS, rule, args);
- }
- else if (!strcmp(act, "isset"))
- {
- parse_args1(ISSET, rule, args);
- }
- else if (!strcmp(act, "isdir"))
- {
- parse_args1(ISDIR, rule, args);
- }
- else if (!strcmp(act, "isfile"))
- {
- parse_args1(ISFILE, rule, args);
- }
- else if (!strcmp(act, "set"))
- {
- parse_args2(SET, rule, args);
- }
- else if (!strcmp(act, "unset"))
- {
- parse_args1(UNSET, rule, args);
- }
- else if (!strcmp(act, "exec"))
- {
- parse_args1(EXEC, rule, args);
- }
- else if (!strcmp(act, "launch"))
- {
- parse_args1(LAUNCH, rule, args);
- }
- else if (!strcmp(act, "matches"))
- {
- parse_args2(MATCHES, rule, args);
- }
- else
- {
- printf("error: unknown action '%s'\n", act);
- exit(1);
- }
- return rule;
-}
-
-static void add_ruleset(Rule** rules, size_t nrules, Rule* rule)
-{
- if (rules)
- {
- rules = realloc(rules, sizeof(Rule) * ++nrules);
- rules[nrules-1] = rule;
- }
- Rulesets = realloc(Rulesets, sizeof(Rule) * ++NumRulesets);
- Rulesets[NumRulesets-1] = rules;
-}
-
-static void parse_rules(char* path)
-{
- char* line = NULL;
- char* raw = readfile(path);
- telem_send("RULES: %s\n", path);
- if (raw)
- {
- Rule** rules = NULL;
- size_t nrules = 0;
-
- while ((line = nextline(&raw)) != NULL)
- {
- char* cmd = line;
- /* skip whitespace */
- for (; *cmd && isspace(*cmd); cmd++);
-
- if (!*cmd)
- {
- add_ruleset(rules, nrules, calloc(1, sizeof(Rule)));
- rules = NULL;
- nrules = 0;
- }
- else if (*cmd != '#')
- {
- char* action = nextfield(&cmd);
- rules = realloc(rules, sizeof(Rule) * ++nrules);
- rules[nrules-1] = parse_rule(action, cmd);
- }
- else
- {
- free(line);
- }
- line = NULL;
- }
-
- if (rules)
- {
- add_ruleset(rules, nrules, calloc(1, sizeof(Rule)));
- }
- }
-}
-
-static void check_ruleset(Rule** rule)
-{
- Launched = false;
- for (; (*rule)->type != COMPLETE; rule++)
- {
- telem_send(" RULE(%s '%s' '%s')\n", type2str((*rule)->type), (*rule)->arg1, (*rule)->arg2);
- if (!apply_rule(*rule))
- {
- break;
- }
- }
-
- if (Launched && (*rule)->type == COMPLETE)
- {
- telem_send(" ACCEPT\n");
- exit(0);
- }
- else
- {
- telem_send(" NEXT\n");
- }
-}
-
-int main(int argc, char** argv)
-{
- ARGV0 = *argv;
- if (argc != 2)
- {
- usage(ARGV0);
- }
- else
- {
- /* load rules from files */
- parse_rules("fetch.rules");
- parse_rules(homedir_path("config/tide/fetch.rules"));
- add_ruleset(NULL, 0, NULL); // terminate the parsed set of rules
-
- telem_send("FETCH(%s)\n", argv[1]);
- setenv("data", argv[1], 1);
-
- /* process parsed rules */
- for (; Rulesets && *Rulesets; Rulesets++)
- {
- check_ruleset(*Rulesets);
- }
-
- /* process builtin rules */
- for (unsigned int i = 0; BuiltinRules[i]; i++)
- {
- check_ruleset(BuiltinRules[i]);
- }
- }
- return 1;
-}
+++ /dev/null
-= pick(1)
-:doctype: manpage
-
-== NAME
-
-registrar - registry service for tracking open tide(1) windows
-
-== SYNOPSIS
-
-*registrar*
-
-== DESCRIPTION
-
-
-== OPTIONS
-
-
-== ENVIRONMENT
-
-
-== SEE ALSO
-
-registrar(1), tide(1), edit(1), fetch(1), fetchsel(1)
\ No newline at end of file
+++ /dev/null
-#include <stdc.h>
-#include <vec.h>
-#include <x11.h>
-
-#define INCLUDE_DEFS
-#include "config.h"
-
-static void xkeypress(XConf* x, XEvent* e);
-static void xbtnpress(XConf* x, XEvent* e);
-static void redraw(XConf* x);
-static void xinit(XConf* x);
-
-typedef struct {
- float score;
- char* string;
- size_t length;
- size_t match_start;
- size_t match_end;
-} Choice;
-
-char Query[8192] = {0};
-size_t QueryIdx = 0;
-vec_t Choices = {0};
-size_t ChoiceIdx = 0;
-size_t Offset = 0;
-
-static int peekc(FILE* in)
-{
- int c = fgetc(in);
- ungetc(c, in);
- return c;
-}
-
-static char* rdline(FILE* fin, size_t* len)
-{
- if (feof(fin) || ferror(fin))
- return NULL;
- size_t size = 256;
- size_t index = 0;
- char* str = (char*)calloc(size,1);
- while (!feof(stdin))
- {
- if (index+2 >= size)
- {
- size = size << 1;
- str = realloc(str, size);
- }
- char ch = fgetc(fin);
- if (ch == '\r') continue;
- if (ch == EOF || ch == '\n') break;
- str[index++] = ch;
- str[index] = '\0';
- }
- if (len) *len = index;
- return str;
-}
-
-static int by_score(const void* a, const void* b)
-{
- Choice* ca = ((Choice*)a);
- Choice* cb = ((Choice*)b);
- if (ca->score < cb->score)
- return 1;
- else if (ca->score > cb->score)
- return -1;
- else
- return strcmp(ca->string, cb->string);
-}
-
-static void load_choices(XConf* x)
-{
- size_t choice_len;
- char* choice_text;
- bool shown = false;
- Choice choice = {0};
- vec_init(&Choices, sizeof(Choice));
- while ((choice_text = rdline(stdin, &choice_len)) != NULL)
- {
- if (choice_len > 0)
- {
- choice.string = choice_text;
- choice.length = strlen(choice_text);
- choice.score = 1.0;
- if (!shown && peekc(stdin) != EOF)
- {
- x11_show(x);
- redraw(x);
- shown = true;
- }
- vec_push_back(&Choices, &choice);
- }
- else
- {
- free(choice_text);
- }
- }
-}
-
-static char* find_char(char* str, int ch)
-{
- for (; *str; str++)
- if (tolower(*str) == tolower(ch))
- return str;
- return NULL;
-}
-
-static inline bool find_match(char *string, char* query, size_t offset, char **start, char **end)
-{
- char *s, *e;
- /* find first match char or bail */
- s = e = find_char(&string[offset], *query);
- if (s == NULL) return false;
-
- /* find the end of the match */
- for (query++; *query; query++)
- if ((e = find_char(e+1, *query)) == NULL)
- return false;
-
- /* if we made it this far, we found a match */
- *start = s, *end = e;
- return true;
-}
-
-static bool match(char *string, size_t offset, size_t *start, size_t *end)
-{
- char *s1 = 0, *e1 = 0, *s2, *e2;
- /* first check if we match at all */
- if (!find_match(string, Query, offset, &s1, &e1))
- return false;
- s2 = s1, e2 = e1; // Init s2 and e2 before use below
-
- /* next find the longest match. If multiple, take the left most one */
- while (find_match(string, Query, ++offset, &s1, &e1) && ((e1-s1) <= (e2-s2)))
- s1 = s2, e1 = e2;
-
- /* return the best match */
- *start = s1 - string;
- *end = e1 - string;
- return true;
-}
-
-static void score(void)
-{
- unsigned int nchoices = vec_size(&Choices);
- for (unsigned int i = 0; i < nchoices; i++) {
- Choice* choice = (Choice*)vec_at(&Choices, i);
- float qlen = (float)QueryIdx;
- if (match(choice->string, 0, &choice->match_start, &choice->match_end))
- {
- float clen = (float)(choice->match_end - choice->match_start) + 1;
- choice->score = qlen / clen / (float)(choice->length);
- }
- else
- {
- choice->match_start = 0;
- choice->match_end = 0;
- choice->score = 0.0f;
- }
- }
- vec_sort(&Choices, by_score);
-}
-
-int main(int argc, char** argv)
-{
- XConf x = {0};
- if (argc >= 2) {
- size_t sz = min(strlen(argv[1]), sizeof(Query)-1);
- strncpy(Query, argv[1], sz);
- QueryIdx = sz;
- }
- xinit(&x);
- load_choices(&x);
- score();
- if (vec_size(&Choices) > 1)
- x11_event_loop(&x, redraw);
- Choice* choice = (Choice*)vec_at(&Choices, ChoiceIdx);
- if (vec_size(&Choices) && ChoiceIdx != SIZE_MAX)
- printf("%s\n", choice->string);
- return 0;
-}
-
-static void xkeypress(XConf* x, XEvent* e)
-{
- char buf[8];
- KeySym key;
- Status status;
- if (x->xic)
- Xutf8LookupString(x->xic, &(e->xkey), buf, sizeof(buf), &key, &status);
- else
- XLookupString(&(e->xkey), buf, sizeof(buf), &key, 0);
- if (key == XK_Return)
- {
- x->state = QUITTING;
- }
- else if (key == XK_Escape)
- {
- x->state = QUITTING;
- ChoiceIdx = SIZE_MAX;
- }
- else if (key == XK_Up)
- {
- if (ChoiceIdx > 0)
- ChoiceIdx--;
- if (ChoiceIdx < Offset)
- Offset--;
- }
- else if (key == XK_Down)
- {
- size_t nlines = ((x->height - x->font->height - 4) / x->font->height) - 1;
- size_t maxidx = Offset + nlines;
- if (ChoiceIdx+1 < vec_size(&Choices))
- ChoiceIdx++;
- if (ChoiceIdx > maxidx)
- Offset++;
- }
- else if (key == XK_BackSpace)
- {
- if (QueryIdx > 0)
- Query[--QueryIdx] = '\0';
- score();
- }
- else if (key >= 0x20 && key <= 0x7F)
- {
- if (QueryIdx < sizeof(Query)-1)
- Query[QueryIdx++] = key;
- Offset = ChoiceIdx = 0;
- score();
- }
-}
-
-static void xbtnpress(XConf* x, XEvent* e)
-{
- (void)x;
- if (e->xbutton.button == Button1)
- {
- int starty = x->font->height + 4;
- e->xbutton.y = (e->xbutton.y < starty ? starty : e->xbutton.y - starty);
- ChoiceIdx = Offset + (e->xbutton.y / x->font->height);
- if (ChoiceIdx >= vec_size(&Choices))
- ChoiceIdx = vec_size(&Choices)-1;
- }
- else if (e->xbutton.button == Button2)
- {
- x->state = QUITTING;
- }
- else if (e->xbutton.button == Button3)
- {
- x->state = QUITTING;
- ChoiceIdx = SIZE_MAX;
- }
- else if (e->xbutton.button == Button4)
- {
- if (Offset > 0) Offset--;
- }
- else if (e->xbutton.button == Button5)
- {
- if (Offset < vec_size(&Choices)) Offset++;
- }
-}
-
-static void redraw(XConf* x)
-{
- /* draw the background colors and border */
- size_t fheight = x->font->height;
- size_t nlines = ((x->height - x->font->height - 4) / x->font->height) - 1;
- size_t scroll_height = x->height - fheight - 4;
- size_t start = (size_t)((float)Offset / (float)vec_size(&Choices) * scroll_height);
- size_t size = (size_t)((float)nlines / (float)vec_size(&Choices) * scroll_height);
-
- x11_draw_rect(x, Palette[EditBg], 0, 0, x->width, x->height);
- x11_draw_rect(x, Palette[TagsBg], 0, 0, x->width, fheight + 4);
- x11_draw_rect(x, Palette[HorBdr], 0, fheight + 4, x->width, 1);
- x11_draw_rect(x, Palette[ScrollBg], 0, fheight + 5, ScrollWidth+1, scroll_height);
- x11_draw_rect(x, Palette[ScrollFg], 0, fheight + 5 + start, ScrollWidth, (size ? size : 5));
-
- /* get size of the query when printed */
- XGlyphInfo glyphinfo;
- XftTextExtentsUtf8(x->display, x->font, (const FcChar8*)Query, strlen(Query), &glyphinfo);
- int offset = 0;
- if (glyphinfo.width >= (x->width - 4))
- offset = ((x->width - 4) - glyphinfo.width);
-
- /* draw the query and the cursor to the query region */
- int posx = 2 + glyphinfo.width + offset;
- x11_draw_rect(x, Palette[TagsFg], posx-1, 2, 3, 3);
- x11_draw_rect(x, Palette[TagsFg], posx, 2, 1, fheight);
- x11_draw_rect(x, Palette[TagsFg], posx-1, 2+fheight-3, 3, 3);
- x11_draw_string(x, x->font, offset + 2, fheight, Palette[TagsFg], Query);
-
- /* draw the scored and sorted results */
- if (vec_size(&Choices))
- {
- for (int i = Offset, y = 2 * fheight + 4; ((size_t)i < vec_size(&Choices)) && ((size_t)i <= Offset+nlines); i++, y += fheight)
- {
- if ((size_t)i == ChoiceIdx)
- x11_draw_rect(x, Palette[EditSel], ScrollWidth+3, y - x->font->ascent, x->width, fheight);
- x11_draw_string(x, x->font, ScrollWidth+3, y, Palette[TagsFg], ((Choice*)vec_at(&Choices, i))->string);
- }
- }
- else
- {
- x11_draw_string(x, x->font, ScrollWidth+3, 2 * fheight + 4, Palette[TagsFg], "Loading...");
- }
- x11_flip(x);
-}
-
-static void xinit(XConf* x)
-{
- x11_init(x);
- x11_mkdialog(x, 640, 480, 0
- | StructureNotifyMask
- | KeyPressMask
- | ButtonPressMask
- | ExposureMask
- );
- x11_init_gc(x);
- x11_centerwin(x);
- if (!(x->font = x11_font_load(x, Fonts[0])))
- {
- perror("unable to load base font");
- exit(EXIT_FAILURE);
- }
- x->eventfns[KeyPress] = xkeypress;
- x->eventfns[ButtonPress] = xbtnpress;
-}
+++ /dev/null
-= registrar(1)
-:doctype: manpage
-
-== NAME
-
-registrar - registry service for tracking open tide(1) windows
-
-== SYNOPSIS
-
-*registrar*
-
-== DESCRIPTION
-
-
-== SEE ALSO
-
-tide(1), edit(1), pick(1), fetch(1), fetchsel(1)
\ No newline at end of file
+++ /dev/null
-#include <stdc.h>
-#include <x11.h>
-#include <io.h>
-#include "config.h"
-
-typedef struct TWindow {
- struct TWindow* next;
- Window win;
- char* path;
- char* host;
-} TWindow;
-
-Atom XA_REGISTRAR, XA_ADD, XA_DEL, XA_OPEN, XA_DONE, XA_TIDE;
-TWindow* Windows = NULL;
-
-static void* readprop(XConf* x, Window win, char* prop, Atom type, size_t* length)
-{
- Atom rtype, xa_prop = XInternAtom(x->display, prop, False);
- int format;
- unsigned long datalen, nleft;
- unsigned char* data = NULL;
- XGetWindowProperty(
- x->display, win, xa_prop, 0, -1, False, type,
- &rtype, &format, &datalen, &nleft, &data);
- if (length) *length = datalen;
- if (rtype != type)
- {
- if (data) XFree(data);
- data = 0;
- if (length) *length = 0;
- }
- return (void*)data;
-}
-
-static char* read_path(XConf* x, Window id)
-{
- int nprops;
- char* path = NULL;
- Atom xa_file = XInternAtom(x->display, "FILE", False);
- Atom* props = XListProperties(x->display, id, &nprops);
- if (props)
- {
- for (int i = 0; i < nprops; i++)
- {
- if (props[i] == xa_file)
- {
- path = readprop(x, id, "FILE", XA_STRING, 0);
- break;
- }
- }
- XFree(props);
- }
- return path;
-}
-
-static void win_add(XConf* x, Window id)
-{
- char* path = read_path(x, id);
- if (!path) return;
- telem_send("WIN_ADD(0x%x: '%s')\n", (unsigned int)id, path);
- TWindow* win = calloc(1, sizeof(TWindow));
- win->win = id;
- win->next = Windows;
- win->path = path;
- win->host = readprop(x, id, "HOST", XA_STRING, NULL);
- Windows = win;
-}
-
-static void win_del(Window id)
-{
- if (!Windows) return;
- telem_send("WIN_DEL(0x%x)\n", id);
- if (Windows->win == id)
- {
- TWindow* deadite = Windows;
- Windows = deadite->next;
- free(deadite);
- }
- else
- {
- TWindow* w = Windows;
- for (;w && w->next && (w->next->win != id); w = w->next);
- if (w && w->next)
- {
- TWindow* deadite = w->next;
- w->next = deadite->next;
- free(deadite->path), deadite->path = NULL;
- free(deadite->host), deadite->host = NULL;
- free(deadite);
- }
- }
-}
-
-static void win_send(XConf* x, Window from, Window to, int mask, char* atom, size_t val)
-{
- telem_send("WIN_SEND(from: %lx, to: %lx, atom: %d)\n", from, to, atom);
- XEvent ev = {0};
- ev.xclient.type = ClientMessage;
- ev.xclient.send_event = True;
- ev.xclient.message_type = XInternAtom(x->display, atom, False);
- ev.xclient.window = from;
- ev.xclient.format = 32;
- ev.xclient.data.l[0] = val;
- XSendEvent(x->display, to, False, mask, &ev);
-}
-
-static void win_open(XConf* x, Window winid, char* path, char* addr, char* host)
-{
- if (path)
- {
- telem_send("WIN_OPEN(%lx '%s' '%s')\n", winid, path, host);
- /* search for an existing window */
- for (TWindow* win = Windows; win; win = win->next)
- {
- /* refresh the filepath and crudely determine if window still valid */
- free(win->path);
- win->path = NULL;
- char* file = readprop(x, win->win, "FILE", XA_STRING, NULL);
- win->path = (file ? file : NULL);
- telem_send(" %lx '%s' '%s'\n", win->win, win->path, win->host);
- if (win->host && !strcmp(win->host, host) &&
- win->path && !strcmp(win->path, path))
- {
- /* double check that the window id didnt get reassigned to a non-tide window */
- telem_send("WIN_ACTIVATE(0x%x '%s')\n", (unsigned int)win->win, win->path);
- x11_error_clear();
- char* type = readprop(x, win->win, "TIDE", XA_STRING, 0);
- if (!type || x11_error_get())
- {
- free(win->path), win->path = NULL;
- free(win->host), win->host = NULL;
- free(type);
- break;
- }
- else
- {
- XEvent ev = {0};
- ev.xclient.type = ClientMessage;
- ev.xclient.send_event = True;
- ev.xclient.message_type = XInternAtom(x->display, "_NET_ACTIVE_WINDOW", False);
- ev.xclient.window = win->win;
- ev.xclient.format = 32;
- ev.xclient.data.l[0] = 1;
- ev.xclient.data.l[1] = CurrentTime;
- ev.xclient.data.l[2] = 0;
- XSendEvent(x->display, x->root, False, SubstructureRedirectMask|SubstructureNotifyMask, &ev);
- win_send(x, x->self, win->win, 0, "GOTO", strtoul(addr, NULL, 0));
- win_send(x, x->self, winid, 0, "DONE", 0);
- free(type);
- return;
- }
- }
- }
-
- /* if we don't find it, tell sender to spawn a new one */
- win_send(x, x->self, winid, 0, "OPEN", 0);
- }
-}
-
-static void selclear(XConf* x, XEvent* e)
-{
- (void)e;
- XSync(x->display, False);
- telem_send("EXITING\n");
- exit(0);
-}
-
-static void clientmsg(XConf* x, XEvent* e)
-{
- if (XA_REGISTRAR != e->xclient.message_type)
- return;
- if (XA_ADD == (Atom)(e->xclient.data.l[0]))
- win_add(x, e->xclient.window);
- else if (XA_DEL == (Atom)(e->xclient.data.l[0]))
- win_del(e->xclient.window);
- XSync(x->display, False);
-}
-
-static TWindow* win_sweep(TWindow* win)
-{
- if (win)
- {
- if (win->path)
- {
- win->next = win_sweep(win->next);
- }
- else
- {
- TWindow* dead = win;
- telem_send("WIN_SWEEP(%x)\n", (unsigned)win->win);
- win = win_sweep(win->next);
- free(dead);
- }
- }
- return win;
-}
-
-static void propnotify(XConf* x, XEvent* e)
-{
- (void)e;
- Atom type;
- int format;
- unsigned long datalen, nleft;
- unsigned char* data = NULL;
- XGetWindowProperty(
- x->display, x->self, XA_OPEN, 0, -1, True, XA_WINDOW,
- &type, &format, &datalen, &nleft, &data);
- /* handle all of the window requests */
- for (Window* win = (Window*)data; datalen && win && *win; win++, datalen--)
- {
- char* file = readprop(x, *win, "FILE", XA_STRING, NULL);
- char* addr = readprop(x, *win, "ADDR", XA_STRING, NULL);
- char* host = readprop(x, *win, "HOST", XA_STRING, NULL);
- win_open(x, *win, file, (addr ? addr : "0"), host);
- if(file) XFree(file);
- if(addr) XFree(addr);
- }
- XSync(x->display, False);
-
- /* cleanup any invalid windows */
- Windows = win_sweep(Windows);
-}
-
-static void find_windows(XConf* x)
-{
- XGrabServer(x->display);
- size_t nwindows = 0;
- Window* windows = readprop(x, x->root, "_NET_CLIENT_LIST", XA_WINDOW, &nwindows);
- telem_send("FIND_WINS(nwins: %lu)\n", nwindows);
- XUngrabServer(x->display);
- for (size_t i = 0; i < nwindows; i++)
- win_add(x, windows[i]);
- if (windows) XFree(windows);
-}
-
-static int daemonize(void)
-{
- int status;
- /* fork into the background first */
- if ((status = fork()) < 0)
- return -1;
- else if (status > 0)
- _exit(0);
-
- /* create a new session */
- if (setsid() < 0) return -1;
-
- /* fork again so we don't reattach to the terminal */
- if ((status = fork()) < 0)
- return -1;
- else if (status > 0)
- _exit(0);
-
- /* clear any inherited umask(2) value */
- umask(0);
- (void)chdir("/");
- close(0), close(1), close(2);
- return 0;
-}
-
-int main(int argc, char** argv)
-{
- (void)argc, (void)argv;
- ARGV0 = argv[0];
- if (daemonize() >= 0)
- {
- XConf x = {0};
- x11_init(&x);
- x11_mkwin(&x, 1, 1, PropertyChangeMask);
- XA_REGISTRAR = XInternAtom(x.display, "TIDE_REGISTRAR", 0);
- XA_ADD = XInternAtom(x.display, "ADD", 0);
- XA_DEL = XInternAtom(x.display, "DEL", 0);
- XA_OPEN = XInternAtom(x.display, "OPEN", 0);
- XA_DONE = XInternAtom(x.display, "DONE", 0);
- XA_TIDE = XInternAtom(x.display, "TIDE", 0);
- x.eventfns[SelectionClear] = selclear;
- x.eventfns[ClientMessage] = clientmsg;
- x.eventfns[PropertyNotify] = propnotify;
- if (None == XGetSelectionOwner(x.display, XA_REGISTRAR))
- {
- XSetSelectionOwner(x.display, XA_REGISTRAR, x.self, CurrentTime);
- if (x.self == XGetSelectionOwner(x.display, XA_REGISTRAR))
- {
- find_windows(&x);
- x11_event_loop(&x, 0);
- }
- else
- {
- telem_send("FAILED\n");
- }
- }
- }
- return 1;
-}