summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbryan <bryan@veltar.systems>2026-08-27 13:33:41 +0200
committerbryan <bryan@veltar.systems>2026-08-27 13:33:41 +0200
commitf92c62127cdbe6fb0831e911c5c6dc2b5db7a7c4 (patch)
treefb5659b274bb5117a93c4f021e2a654858c7d371
downloadselc-f92c62127cdbe6fb0831e911c5c6dc2b5db7a7c4.tar.gz
selc-f92c62127cdbe6fb0831e911c5c6dc2b5db7a7c4.zip
Initial import
-rw-r--r--.gitignore3
-rw-r--r--LICENSE15
-rw-r--r--Makefile34
-rw-r--r--README60
-rw-r--r--config.def.h22
-rw-r--r--config.mk19
-rw-r--r--selc.c548
7 files changed, 701 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..189fb68
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+config.h
+selc.exe
+*.o
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..a1111f5
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,15 @@
+ISC License
+
+© 2026 selc authors
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..feab946
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,34 @@
+.POSIX:
+
+include config.mk
+
+HDR = config.h
+SRC = selc.c
+
+all: selc$(EXE)
+
+config.h:
+ copy /Y config.def.h config.h
+
+selc$(EXE): $(SRC) $(HDR)
+ $(CC) $(CFLAGS) -o $@ $(SRC) $(LDFLAGS)
+
+clean:
+ -$(RM) selc$(EXE)
+
+install: all
+ -mkdir "$(subst /,\,$(DESTDIR)$(BINDIR))"
+ copy /Y selc$(EXE) "$(subst /,\,$(DESTDIR)$(BINDIR)/selc$(EXE))"
+ @echo installed to $(DESTDIR)$(BINDIR)/selc$(EXE)
+
+uninstall:
+ -$(RM) "$(subst /,\,$(DESTDIR)$(BINDIR)/selc$(EXE))"
+
+boot: install
+ "$(subst /,\,$(DESTDIR)$(BINDIR))\selc$(EXE)" -i
+
+unboot:
+ -"$(subst /,\,$(DESTDIR)$(BINDIR))\selc$(EXE)" -u
+ -$(RM) "$(subst /,\,$(DESTDIR)$(BINDIR)/selc$(EXE))"
+
+.PHONY: all clean install uninstall boot unboot
diff --git a/README b/README
new file mode 100644
index 0000000..e9a2379
--- /dev/null
+++ b/README
@@ -0,0 +1,60 @@
+selc - select-copy
+
+ selc [-i] [-k] [-u] [-v]
+
+Windows X11-PRIMARY analogue: selecting text copies it. A drag,
+double-click, shift+click, or shift+arrow selection is copied to
+the clipboard. No tray, no window, one process, a mouse hook that
+only snapshots points.
+
+ selc run (no-op if already running)
+ selc -i write HKCU Run (start at logon) and run
+ selc -u remove HKCU Run and stop
+ selc -k stop the running instance
+ selc -v run with a console; print copy method
+
+
+Build
+
+ make
+ make install
+ make boot
+
+boot is install plus selc -i. The binary is a Windows subsystem
+image; launching it from Explorer or Run does not open a console.
+as(1) and ld(1) must be the MinGW ones; config.mk prepends
+MINGWBIN to PATH.
+
+Default install path: %USERPROFILE%\.local\bin
+Override: make PREFIX=... install
+
+
+How
+
+A WH_MOUSE_LL hook (and optionally WH_KEYBOARD_LL) records the
+gesture on the hook thread and posts to a message-only window.
+The window waits COPYWAIT ms so the target app can finish the
+selection, then:
+
+ 1. ignore non-client hits (caption, scrollbars, resize)
+ 2. ignore password Edit controls
+ 3. WM_COPY to the focused child (SendMessageTimeout)
+ 4. if the clipboard sequence did not change, inject Ctrl+C
+ except into console classes listed in config.h
+
+Logon autostart is HKCU\...\Run\selc, so it starts with the
+interactive session. That is the right time; a clipboard helper
+cannot run in session 0.
+
+
+Remarks
+
+Injected Ctrl+C is skipped for ConsoleWindowClass, Windows
+Terminal, and mintty so a false gesture cannot interrupt a
+job. Those hosts often already copy on select.
+
+UIPI: an unelevated selc cannot copy from an elevated window.
+Run it at the same integrity level as the programs you use.
+
+Edit config.h (copied from config.def.h) for thresholds and
+the no-Ctrl+C class list. Recompile.
diff --git a/config.def.h b/config.def.h
new file mode 100644
index 0000000..357bcba
--- /dev/null
+++ b/config.def.h
@@ -0,0 +1,22 @@
+/* Copy to config.h and edit. Recompile after changing knobs. */
+
+enum {
+ MINPIX = 4, /* min drag distance in pixels */
+ COPYWAIT = 25, /* ms after the gesture before we copy */
+ COPYTO = 50, /* SendMessageTimeout budget for WM_COPY */
+ HITTO = 50, /* same for WM_NCHITTEST */
+ DBLCOPY = 1, /* copy on double/triple click */
+ SHIFTCLICK = 1, /* copy on shift+click (extend selection) */
+ KEYSEL = 1, /* copy on shift+arrows/home/end/pgup/pgdn */
+ CTRLA = 0 /* copy after Ctrl+A; 0: Explorer would copy files */
+};
+
+/*
+ * Root window classes that must not receive injected Ctrl+C.
+ * WM_COPY is still tried. Console hosts treat Ctrl+C as interrupt.
+ */
+static const char *nocctrlc[] = {
+ "ConsoleWindowClass",
+ "CASCADIA_HOSTING_WINDOW_CLASS",
+ "mintty",
+};
diff --git a/config.mk b/config.mk
new file mode 100644
index 0000000..1da3239
--- /dev/null
+++ b/config.mk
@@ -0,0 +1,19 @@
+# selc — site/toolchain knobs. Edit this, not the Makefile.
+#
+# Windows only. Install: make install
+# default: %USERPROFILE%\.local\bin
+# Boot: make boot (install + HKCU Run, then starts selc)
+# Override: make PREFIX=... install
+
+PREFIX ?= $(USERPROFILE)/.local
+BINDIR ?= $(PREFIX)/bin
+
+# MinGW-w64 gcc. LLVM clang in PATH targets MSVC and needs the VS
+# developer prompt; MinGW does not. as/ld live next to gcc.
+MINGWBIN = C:/msys64/mingw64/bin
+CC = $(MINGWBIN)/gcc
+CFLAGS = -std=c99 -Wall -Wextra -pedantic -Os
+LDFLAGS = -mwindows -luser32 -ladvapi32
+EXE = .exe
+RM = cmd /c del /q
+export PATH := $(MINGWBIN);$(PATH)
diff --git a/selc.c b/selc.c
new file mode 100644
index 0000000..1b8eb86
--- /dev/null
+++ b/selc.c
@@ -0,0 +1,548 @@
+/* See LICENSE file for copyright and license details. */
+#define WINVER 0x0601
+#define _WIN32_WINNT 0x0601
+#define WIN32_LEAN_AND_MEAN
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <windows.h>
+
+#include "config.h"
+
+#define LEN(x) (sizeof(x) / sizeof((x)[0]))
+
+enum {
+ WM_SEL = WM_APP + 1
+};
+
+struct Sel {
+ POINT down;
+ POINT up;
+ int dragged;
+ int clicks;
+ int shift;
+ int kbd;
+};
+
+static char *argv0;
+static int verbose;
+static HWND hwnd;
+static HHOOK mousehook;
+static HHOOK kbdhook;
+static HANDLE quit;
+static struct Sel sel;
+static struct Sel pending;
+
+static void die(const char *fmt, ...);
+static void usage(void);
+static void attachio(int force);
+static int samewin(HWND a, HWND b);
+static int isclient(HWND hw, POINT pt);
+static int ispass(HWND hw);
+static int skipctrlc(HWND hw);
+static int goodsel(const struct Sel *s);
+static void sendctrlc(void);
+static void docopy(void);
+static void request(int kbd);
+static LRESULT CALLBACK mouseproc(int code, WPARAM wp, LPARAM lp);
+static LRESULT CALLBACK kbdproc(int code, WPARAM wp, LPARAM lp);
+static LRESULT CALLBACK wndproc(HWND hw, UINT msg, WPARAM wp,
+ LPARAM lp);
+static int runkey(int add);
+static void signalquit(void);
+static int loop(HINSTANCE inst);
+
+static void
+die(const char *fmt, ...)
+{
+ va_list ap;
+ size_t n;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ n = strlen(fmt);
+ if (n && fmt[n - 1] == ':')
+ fprintf(stderr, " %lu\n", GetLastError());
+ else
+ fputc('\n', stderr);
+ exit(1);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: %s [-i] [-k] [-u] [-v]\n",
+ argv0 ? argv0 : "selc");
+ exit(2);
+}
+
+static void
+attachio(int force)
+{
+ if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
+ if (!force)
+ return;
+ AllocConsole();
+ }
+ freopen("CONOUT$", "w", stdout);
+ freopen("CONOUT$", "w", stderr);
+}
+
+static int
+samewin(HWND a, HWND b)
+{
+ if (!a || !b)
+ return 0;
+ if (a == b)
+ return 1;
+ return GetAncestor(a, GA_ROOT) == GetAncestor(b, GA_ROOT);
+}
+
+static int
+isclient(HWND hw, POINT pt)
+{
+ DWORD_PTR ht;
+ LPARAM lp;
+
+ lp = MAKELPARAM((short)pt.x, (short)pt.y);
+ if (!SendMessageTimeoutA(hw, WM_NCHITTEST, 0, lp,
+ SMTO_ABORTIFHUNG | SMTO_NORMAL, HITTO, &ht))
+ return 0;
+ return ht == HTCLIENT;
+}
+
+static int
+ispass(HWND hw)
+{
+ char cls[32];
+ LONG style;
+
+ if (!hw)
+ return 0;
+ if (!GetClassNameA(hw, cls, (int)sizeof(cls)))
+ return 0;
+ if (strcmp(cls, "Edit") != 0)
+ return 0;
+ style = GetWindowLongA(hw, GWL_STYLE);
+ return (style & ES_PASSWORD) != 0;
+}
+
+static int
+skipctrlc(HWND hw)
+{
+ char cls[64];
+ HWND root;
+ int i;
+
+ root = GetAncestor(hw, GA_ROOT);
+ if (!root)
+ root = hw;
+ if (!GetClassNameA(root, cls, (int)sizeof(cls)))
+ return 0;
+ for (i = 0; i < (int)LEN(nocctrlc); i++)
+ if (strcmp(cls, nocctrlc[i]) == 0)
+ return 1;
+ return 0;
+}
+
+static int
+goodsel(const struct Sel *s)
+{
+ HWND a, b;
+
+ a = WindowFromPoint(s->down);
+ b = WindowFromPoint(s->up);
+ if (!a || !isclient(a, s->down))
+ return 0;
+ if (!samewin(a, b))
+ return 0;
+ return 1;
+}
+
+static void
+sendctrlc(void)
+{
+ INPUT in[4];
+ int n, ctrl;
+
+ ctrl = (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0;
+ memset(in, 0, sizeof(in));
+ n = 0;
+ if (!ctrl) {
+ in[n].type = INPUT_KEYBOARD;
+ in[n].ki.wVk = VK_CONTROL;
+ n++;
+ }
+ in[n].type = INPUT_KEYBOARD;
+ in[n].ki.wVk = 'C';
+ n++;
+ in[n].type = INPUT_KEYBOARD;
+ in[n].ki.wVk = 'C';
+ in[n].ki.dwFlags = KEYEVENTF_KEYUP;
+ n++;
+ if (!ctrl) {
+ in[n].type = INPUT_KEYBOARD;
+ in[n].ki.wVk = VK_CONTROL;
+ in[n].ki.dwFlags = KEYEVENTF_KEYUP;
+ n++;
+ }
+ SendInput((UINT)n, in, sizeof(INPUT));
+}
+
+static void
+docopy(void)
+{
+ HWND fg, foc;
+ DWORD tid, mine, seq;
+ DWORD_PTR dummy;
+ int attached;
+
+ if (GetAsyncKeyState(VK_MENU) & 0x8000)
+ return;
+ fg = GetForegroundWindow();
+ if (!fg)
+ return;
+ mine = GetCurrentThreadId();
+ tid = GetWindowThreadProcessId(fg, NULL);
+ attached = 0;
+ if (tid && tid != mine)
+ attached = AttachThreadInput(mine, tid, TRUE);
+ foc = GetFocus();
+ if (!foc)
+ foc = fg;
+ if (ispass(foc))
+ goto done;
+ seq = GetClipboardSequenceNumber();
+ SendMessageTimeoutA(foc, WM_COPY, 0, 0,
+ SMTO_ABORTIFHUNG | SMTO_NORMAL, COPYTO, &dummy);
+ if (GetClipboardSequenceNumber() != seq) {
+ if (verbose)
+ fprintf(stderr, "selc: WM_COPY\n");
+ goto done;
+ }
+ if (skipctrlc(foc) || skipctrlc(fg)) {
+ if (verbose)
+ fprintf(stderr, "selc: skip Ctrl+C\n");
+ goto done;
+ }
+ sendctrlc();
+ if (verbose)
+ fprintf(stderr, "selc: Ctrl+C\n");
+done:
+ if (attached)
+ AttachThreadInput(mine, tid, FALSE);
+}
+
+static void
+request(int kbd)
+{
+ sel.kbd = kbd;
+ pending = sel;
+ if (hwnd)
+ PostMessageA(hwnd, WM_SEL, 0, 0);
+}
+
+static LRESULT CALLBACK
+mouseproc(int code, WPARAM wp, LPARAM lp)
+{
+ MSLLHOOKSTRUCT *ms;
+ static POINT down, lastpt;
+ static int dragged, clicks, armed;
+ static DWORD lastup;
+ long dx, dy;
+ int dt, cx, cy;
+
+ if (code != HC_ACTION)
+ return CallNextHookEx(mousehook, code, wp, lp);
+ ms = (MSLLHOOKSTRUCT *)lp;
+ if (wp == WM_LBUTTONDOWN) {
+ down = ms->pt;
+ dragged = 0;
+ armed = 1;
+ dt = (int)(ms->time - lastup);
+ cx = GetSystemMetrics(SM_CXDOUBLECLK);
+ cy = GetSystemMetrics(SM_CYDOUBLECLK);
+ if (DBLCOPY && dt >= 0 &&
+ dt <= (int)GetDoubleClickTime() &&
+ abs(ms->pt.x - lastpt.x) <= cx &&
+ abs(ms->pt.y - lastpt.y) <= cy)
+ clicks++;
+ else
+ clicks = 1;
+ lastpt = ms->pt;
+ } else if (wp == WM_MOUSEMOVE) {
+ if (armed && !dragged) {
+ dx = (long)ms->pt.x - down.x;
+ dy = (long)ms->pt.y - down.y;
+ if (dx * dx + dy * dy >=
+ (long)MINPIX * MINPIX)
+ dragged = 1;
+ }
+ } else if (wp == WM_LBUTTONUP) {
+ lastup = ms->time;
+ lastpt = ms->pt;
+ if (armed) {
+ sel.down = down;
+ sel.up = ms->pt;
+ sel.dragged = dragged;
+ sel.clicks = clicks;
+ sel.shift = (GetAsyncKeyState(VK_SHIFT) &
+ 0x8000) != 0;
+ if (sel.dragged ||
+ (DBLCOPY && sel.clicks >= 2) ||
+ (SHIFTCLICK && sel.shift &&
+ sel.clicks == 1))
+ request(0);
+ }
+ armed = 0;
+ dragged = 0;
+ } else if (wp == WM_RBUTTONDOWN || wp == WM_MBUTTONDOWN) {
+ armed = 0;
+ dragged = 0;
+ }
+ return CallNextHookEx(mousehook, code, wp, lp);
+}
+
+static LRESULT CALLBACK
+kbdproc(int code, WPARAM wp, LPARAM lp)
+{
+ KBDLLHOOKSTRUCT *kb;
+ int shift, ctrl, alt, nav, akey;
+
+ if (code != HC_ACTION)
+ return CallNextHookEx(kbdhook, code, wp, lp);
+ if (wp != WM_KEYUP && wp != WM_SYSKEYUP)
+ return CallNextHookEx(kbdhook, code, wp, lp);
+ kb = (KBDLLHOOKSTRUCT *)lp;
+ shift = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
+ ctrl = (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0;
+ alt = (GetAsyncKeyState(VK_MENU) & 0x8000) != 0;
+ nav = 0;
+ switch (kb->vkCode) {
+ case VK_LEFT:
+ case VK_RIGHT:
+ case VK_UP:
+ case VK_DOWN:
+ case VK_HOME:
+ case VK_END:
+ case VK_PRIOR:
+ case VK_NEXT:
+ nav = 1;
+ break;
+ }
+ akey = CTRLA && kb->vkCode == 'A' && ctrl && !shift && !alt;
+ if ((KEYSEL && nav && shift && !alt) || akey)
+ request(1);
+ return CallNextHookEx(kbdhook, code, wp, lp);
+}
+
+static LRESULT CALLBACK
+wndproc(HWND hw, UINT msg, WPARAM wp, LPARAM lp)
+{
+ switch (msg) {
+ case WM_SEL:
+ if (COPYWAIT > 0)
+ SetTimer(hw, 1, COPYWAIT, NULL);
+ else if (pending.kbd || goodsel(&pending))
+ docopy();
+ return 0;
+ case WM_TIMER:
+ if (wp != 1)
+ break;
+ KillTimer(hw, 1);
+ if (pending.kbd || goodsel(&pending))
+ docopy();
+ return 0;
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ return 0;
+ }
+ return DefWindowProcA(hw, msg, wp, lp);
+}
+
+static int
+runkey(int add)
+{
+ HKEY k;
+ LONG e;
+ wchar_t path[MAX_PATH], val[MAX_PATH + 3];
+ DWORD n;
+ const wchar_t *sub =
+ L"Software\\Microsoft\\Windows\\CurrentVersion\\Run";
+
+ e = RegCreateKeyExW(HKEY_CURRENT_USER, sub, 0, NULL, 0,
+ KEY_SET_VALUE | KEY_QUERY_VALUE, NULL, &k, NULL);
+ if (e != ERROR_SUCCESS) {
+ SetLastError((DWORD)e);
+ die("RegCreateKeyEx:");
+ }
+ if (!add) {
+ RegDeleteValueW(k, L"selc");
+ RegCloseKey(k);
+ if (verbose)
+ fprintf(stderr, "selc: uninstalled Run key\n");
+ return 0;
+ }
+ n = GetModuleFileNameW(NULL, path, (DWORD)LEN(path));
+ if (n == 0 || n >= LEN(path)) {
+ RegCloseKey(k);
+ die("GetModuleFileName:");
+ }
+ if (wcschr(path, L' ')) {
+ val[0] = L'"';
+ wcsncpy(val + 1, path, LEN(val) - 3);
+ val[LEN(val) - 2] = L'\0';
+ n = (DWORD)wcslen(val);
+ if (n < LEN(val) - 1) {
+ val[n] = L'"';
+ val[n + 1] = L'\0';
+ }
+ } else {
+ wcsncpy(val, path, LEN(val));
+ val[LEN(val) - 1] = L'\0';
+ }
+ n = (DWORD)((wcslen(val) + 1) * sizeof(wchar_t));
+ e = RegSetValueExW(k, L"selc", 0, REG_SZ, (BYTE *)val, n);
+ RegCloseKey(k);
+ if (e != ERROR_SUCCESS) {
+ SetLastError((DWORD)e);
+ die("RegSetValueEx:");
+ }
+ if (verbose)
+ fprintf(stderr, "selc: installed Run key\n");
+ return 0;
+}
+
+static void
+signalquit(void)
+{
+ HANDLE e;
+
+ e = OpenEventA(EVENT_MODIFY_STATE, FALSE, "Local\\selc-quit");
+ if (!e)
+ return;
+ SetEvent(e);
+ CloseHandle(e);
+}
+
+static int
+loop(HINSTANCE inst)
+{
+ WNDCLASSA wc;
+ MSG msg;
+ DWORD r;
+
+ memset(&wc, 0, sizeof(wc));
+ wc.lpfnWndProc = wndproc;
+ wc.hInstance = inst;
+ wc.lpszClassName = "selc";
+ if (!RegisterClassA(&wc))
+ die("RegisterClass:");
+ hwnd = CreateWindowExA(0, "selc", "selc", WS_OVERLAPPED,
+ 0, 0, 0, 0, HWND_MESSAGE, NULL, inst, NULL);
+ if (!hwnd)
+ die("CreateWindowEx:");
+ mousehook = SetWindowsHookExA(WH_MOUSE_LL, mouseproc, inst, 0);
+ if (!mousehook)
+ die("SetWindowsHookEx mouse:");
+ if (KEYSEL || CTRLA) {
+ kbdhook = SetWindowsHookExA(WH_KEYBOARD_LL, kbdproc,
+ inst, 0);
+ if (!kbdhook)
+ die("SetWindowsHookEx kbd:");
+ }
+ if (verbose)
+ fprintf(stderr, "selc: running\n");
+ for (;;) {
+ r = MsgWaitForMultipleObjects(1, &quit, FALSE,
+ INFINITE, QS_ALLINPUT);
+ if (r == WAIT_OBJECT_0)
+ break;
+ if (r != WAIT_OBJECT_0 + 1)
+ die("MsgWaitForMultipleObjects:");
+ while (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) {
+ if (msg.message == WM_QUIT)
+ goto done;
+ TranslateMessage(&msg);
+ DispatchMessageA(&msg);
+ }
+ }
+done:
+ if (kbdhook)
+ UnhookWindowsHookEx(kbdhook);
+ if (mousehook)
+ UnhookWindowsHookEx(mousehook);
+ return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+ HINSTANCE inst;
+ HANDLE mtx;
+ char *a, *p;
+ int i, iflag, kflag, uflag;
+
+ argv0 = argv[0] ? argv[0] : "selc";
+ iflag = kflag = uflag = 0;
+ attachio(0);
+ for (i = 1; i < argc; i++) {
+ a = argv[i];
+ if (a[0] != '-' || a[1] == '\0')
+ usage();
+ if (a[1] == '-' && a[2] == '\0') {
+ i++;
+ break;
+ }
+ for (p = a + 1; *p; p++)
+ switch (*p) {
+ case 'i':
+ iflag = 1;
+ break;
+ case 'k':
+ kflag = 1;
+ break;
+ case 'u':
+ uflag = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'h':
+ usage();
+ break; /* NOTREACHED */
+ default:
+ usage();
+ }
+ }
+ if (i != argc)
+ usage();
+ if (verbose)
+ attachio(1);
+ if (kflag) {
+ signalquit();
+ return 0;
+ }
+ if (uflag) {
+ runkey(0);
+ signalquit();
+ return 0;
+ }
+ if (iflag)
+ runkey(1);
+
+ inst = GetModuleHandleA(NULL);
+ quit = CreateEventA(NULL, TRUE, FALSE, "Local\\selc-quit");
+ if (!quit)
+ die("CreateEvent:");
+ mtx = CreateMutexA(NULL, TRUE, "Local\\selc-inst");
+ if (!mtx)
+ die("CreateMutex:");
+ if (GetLastError() == ERROR_ALREADY_EXISTS)
+ return 0;
+ return loop(inst);
+}