@@ -6,13 +6,15 @@ endif | |||
neercs_SOURCES = \ | |||
neercs.cpp neercs.h \ | |||
\ | |||
$(old_sources) \ | |||
\ | |||
video/render.cpp video/render.h \ | |||
video/text-render.cpp video/text-render.h \ | |||
video/simple.lolfx \ | |||
video/blurh.lolfx video/blurv.lolfx \ | |||
video/remanency.lolfx video/glow.lolfx video/postfx.lolfx video/radial.lolfx \ | |||
video/text.lolfx | |||
neercs_CPPFLAGS = @LOL_CFLAGS@ @PIPI_CFLAGS@ @CACA_CFLAGS@ | |||
neercs_CPPFLAGS = @LOL_CFLAGS@ @PIPI_CFLAGS@ @CACA_CFLAGS@ -Iold | |||
neercs_LDADD = | |||
neercs_LDFLAGS = $(top_builddir)/src/liblol.a \ | |||
@LOL_LIBS@ @PIPI_LIBS@ @CACA_LIBS@ @UTIL_LIBS@ @PAM_LIBS@ | |||
@@ -31,3 +33,32 @@ SUFFIXES = .lolfx | |||
.lolfx.o: | |||
$(LOLFX_BUILD) | |||
old_sources = \ | |||
old/actions.c \ | |||
old/ansi.c \ | |||
old/attach.c \ | |||
old/client.c \ | |||
old/configuration.c \ | |||
old/effects.c \ | |||
old/grab.c \ | |||
old/help.c \ | |||
old/input.c \ | |||
old/lock.c \ | |||
old/main.c \ | |||
old/mygetopt.c \ | |||
old/mygetopt.h \ | |||
old/mytrace.c \ | |||
old/mytrace.h \ | |||
old/neercs.h \ | |||
old/python/interpreter.c \ | |||
old/python/py_module.c \ | |||
old/python/py_module.h \ | |||
old/recurrent.c \ | |||
old/screen_list.c \ | |||
old/screensaver.c \ | |||
old/screens.c \ | |||
old/server.c \ | |||
old/term.c \ | |||
old/widgets.c \ | |||
old/widgets.h \ | |||
old/wm.c |
@@ -43,7 +43,12 @@ using namespace lol; | |||
#include "neercs.h" | |||
#include "video/render.h" | |||
Neercs::Neercs() | |||
extern "C" | |||
{ | |||
#include "old/neercs.h" | |||
} | |||
Neercs::Neercs(int argc, char **argv) | |||
: m_ready(false), | |||
m_caca(caca_create_canvas(10, 10)), | |||
m_render(new Render(m_caca)), | |||
@@ -140,7 +145,7 @@ int main(int argc, char **argv) | |||
_chdir("../.."); | |||
#endif | |||
new Neercs(); | |||
new Neercs(argc, argv); | |||
new DebugFps(2, 2); | |||
app.ShowPointer(false); | |||
@@ -14,7 +14,7 @@ | |||
class Neercs : public WorldEntity | |||
{ | |||
public: | |||
Neercs(); | |||
Neercs(int argc, char **argv); | |||
virtual ~Neercs(); | |||
char const *GetName() { return "<neercs>"; } | |||
@@ -0,0 +1,63 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net> | |||
* 2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* 2008-2010 Pascal Terjan <pterjan@linuxfr.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#include "config.h" | |||
#include <caca.h> | |||
#include <errno.h> | |||
#include <string.h> | |||
#include <sys/stat.h> | |||
#include "neercs.h" | |||
void dump_to_file(struct screen_list *screen_list) | |||
{ | |||
char filename[14] = "hardcopy.0"; | |||
int n = 0; | |||
struct stat bufstat; | |||
void * export; | |||
size_t len, wrote; | |||
FILE * out; | |||
/* FIXME maybe use glob and get next one directly */ | |||
while (n<9999 && !stat(filename, &bufstat)) | |||
{ | |||
n++; | |||
sprintf(&filename[9], "%d", n); | |||
} | |||
if (n>=9999) | |||
{ | |||
debug("Too many hardcopy files in current directory\n"); | |||
return; | |||
} | |||
export = caca_export_canvas_to_memory(screen_list->cv, "ansi", &len); | |||
if (!export) | |||
{ | |||
debug("Failed to export to ansi\n"); | |||
return; | |||
} | |||
out = fopen(filename, "w"); | |||
if (!out) | |||
{ | |||
debug("Failed to open output file %s: %s\n", filename, strerror(errno)); | |||
return; | |||
} | |||
wrote = fwrite(export, len, 1, out); | |||
if (!wrote) | |||
{ | |||
debug("Failed to write to output file: %s\n", strerror(errno)); | |||
return; | |||
} | |||
free(export); | |||
} |
@@ -0,0 +1,424 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net> | |||
* 2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* 2008-2010 Pascal Terjan <pterjan@linuxfr.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#include <errno.h> | |||
#include <fcntl.h> | |||
#include <glob.h> | |||
#include <limits.h> | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <unistd.h> | |||
#include <sys/socket.h> | |||
#include <sys/un.h> | |||
#include <sys/types.h> | |||
#include <sys/stat.h> | |||
#include <caca.h> | |||
#include "config.h" | |||
#include "neercs.h" | |||
char *build_socket_path(char *socket_dir, char *session_name, | |||
enum socket_type socktype) | |||
{ | |||
char *path, *dir; | |||
path = (char *)malloc(PATH_MAX + 1); | |||
dir = socket_dir; | |||
if (!dir) | |||
dir = getenv("NEERCSDIR"); | |||
if (!dir) | |||
dir = getenv("TMPDIR"); | |||
if (!dir) | |||
dir = "/tmp"; | |||
if (path) | |||
snprintf(path, PATH_MAX + 1, "%s/neercs.%s%s.sock", dir, session_name, | |||
socktype ? "" : ".srv"); | |||
return path; | |||
} | |||
static char *socket_to_session(char const *sockpath) | |||
{ | |||
char *p, *s; | |||
p = strrchr(sockpath, '/'); | |||
if (!p) | |||
{ | |||
debug("Invalid socket path %s", sockpath); | |||
return NULL; | |||
} | |||
p += 8; /* skip neercs. */ | |||
s = strdup(p); | |||
p = strrchr(s, '.'); | |||
*p = '\0'; /* drop .sock */ | |||
p = strrchr(s, '.'); | |||
*p = '\0'; /* drop .srv */ | |||
p = strdup(s); | |||
free(s); | |||
return p; | |||
} | |||
int create_socket(struct screen_list *screen_list, enum socket_type socktype) | |||
{ | |||
int sock; | |||
struct sockaddr_un myaddr; | |||
sock = socket(AF_UNIX, SOCK_DGRAM, 0); | |||
if (sock < 0) | |||
{ | |||
perror("create_socket:socket"); | |||
return errno; | |||
} | |||
memset(&myaddr, 0, sizeof(struct sockaddr_un)); | |||
myaddr.sun_family = AF_UNIX; | |||
strncpy(myaddr.sun_path, screen_list->comm.socket_path[socktype], | |||
sizeof(myaddr.sun_path) - 1); | |||
unlink(screen_list->comm.socket_path[socktype]); | |||
if (bind(sock, (struct sockaddr *)&myaddr, sizeof(struct sockaddr_un)) < 0) | |||
{ | |||
free(screen_list->comm.socket_path[socktype]); | |||
screen_list->comm.socket_path[socktype] = NULL; | |||
close(sock); | |||
perror("create_socket:bind"); | |||
return errno; | |||
} | |||
fcntl(sock, F_SETFL, O_NONBLOCK); | |||
debug("Listening on %s (%d)", screen_list->comm.socket_path[socktype], sock); | |||
screen_list->comm.socket[socktype] = sock; | |||
return 0; | |||
} | |||
char **list_sockets(char *socket_dir, char *session_name) | |||
{ | |||
char *pattern, *dir; | |||
glob_t globbuf; | |||
globbuf.gl_pathv = NULL; | |||
pattern = (char *)malloc(PATH_MAX + 1); | |||
dir = socket_dir; | |||
if (!dir) | |||
dir = getenv("NEERCSDIR"); | |||
if (!dir) | |||
dir = getenv("TMPDIR"); | |||
if (!dir) | |||
dir = "/tmp"; | |||
if (!pattern) | |||
return globbuf.gl_pathv; | |||
if (session_name && strlen(session_name) + strlen(dir) + 13 < PATH_MAX) | |||
sprintf(pattern, "%s/neercs.%s.srv.sock", dir, session_name); | |||
else | |||
snprintf(pattern, PATH_MAX, "%s/neercs.*.srv.sock", dir); | |||
pattern[PATH_MAX] = '\0'; | |||
debug("Looking for sockets in the form %s", pattern); | |||
glob(pattern, 0, NULL, &globbuf); | |||
free(pattern); | |||
return globbuf.gl_pathv; | |||
} | |||
char *connect_socket(struct screen_list *screen_list, | |||
enum socket_type socktype) | |||
{ | |||
int sock; | |||
struct sockaddr_un addr; | |||
debug("Connecting to %s", screen_list->comm.socket_path[socktype]); | |||
/* Open the socket */ | |||
if ((sock = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) | |||
{ | |||
debug("Failed to create socket\n"); | |||
perror("connect_server:socket"); | |||
return NULL; | |||
} | |||
memset(&addr, 0, sizeof(addr)); | |||
addr.sun_family = AF_UNIX; | |||
strcpy(addr.sun_path, screen_list->comm.socket_path[socktype]); | |||
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) | |||
{ | |||
debug("Failed to connect to %s: %s", | |||
screen_list->comm.socket_path[socktype], strerror(errno)); | |||
close(sock); | |||
return NULL; | |||
} | |||
fcntl(sock, F_SETFL, O_NONBLOCK); | |||
screen_list->comm.socket[socktype] = sock; | |||
if (socktype == SOCK_SERVER) | |||
{ | |||
return socket_to_session(screen_list->comm.socket_path[socktype]); | |||
} | |||
else | |||
return NULL; | |||
} | |||
int request_attach(struct screen_list *screen_list) | |||
{ | |||
char buf[41]; | |||
int bytes; | |||
bytes = snprintf(buf, sizeof(buf) - 1, "ATTACH %10d %10d %10d", | |||
caca_get_canvas_width(screen_list->cv), | |||
caca_get_canvas_height(screen_list->cv), | |||
screen_list->delay); | |||
buf[bytes] = '\0'; | |||
debug("Requesting attach: %s", buf); | |||
return write(screen_list->comm.socket[SOCK_SERVER], buf, strlen(buf)) <= 0; | |||
} | |||
static char *select_socket(struct screen_list *screen_list) | |||
{ | |||
char **sockets = NULL, **usable_sockets = NULL; | |||
int i, sock, nb_usable_sockets = 0; | |||
char *ret = NULL; | |||
sockets = list_sockets(screen_list->comm.socket_dir, screen_list->comm.session_name); | |||
if (sockets) | |||
{ | |||
for (i = 0; sockets[i]; i++); | |||
/* Return the socket or NULL if there is not more than one match */ | |||
if (i <= 1) | |||
{ | |||
if (sockets[0]) | |||
ret = strdup(sockets[0]); | |||
goto end; | |||
} | |||
/* Else ask the user to chose one */ | |||
usable_sockets = malloc(i * sizeof(char *)); | |||
for (i = 0; sockets[i]; i++) | |||
{ | |||
struct sockaddr_un addr; | |||
if ((sock = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) | |||
{ | |||
perror("select_socket:socket"); | |||
goto end; | |||
} | |||
memset(&addr, 0, sizeof(addr)); | |||
addr.sun_family = AF_UNIX; | |||
strcpy(addr.sun_path, sockets[i]); | |||
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) | |||
{ | |||
switch (errno) | |||
{ | |||
case EACCES: | |||
debug("Connection refused on %s", sockets[i]); | |||
break; | |||
case ECONNREFUSED: | |||
fprintf(stderr, "%s is dead\n", sockets[i]); | |||
break; | |||
default: | |||
fprintf(stderr, "Unknown error on %s:%s\n", sockets[i], | |||
strerror(errno)); | |||
} | |||
} | |||
else | |||
{ | |||
usable_sockets[nb_usable_sockets] = strdup(sockets[i]); | |||
debug("%s is usable", usable_sockets[nb_usable_sockets]); | |||
nb_usable_sockets++; | |||
close(sock); | |||
} | |||
} | |||
if (!nb_usable_sockets) | |||
goto end; | |||
if (nb_usable_sockets == 1) | |||
{ | |||
ret = strdup(usable_sockets[0]); | |||
goto end; | |||
} | |||
else | |||
{ | |||
caca_event_t ev; | |||
enum caca_event_type t; | |||
int current_line = 1; | |||
int refresh = 1; | |||
screen_list->cv = caca_create_canvas(0, 0); | |||
screen_list->dp = caca_create_display(screen_list->cv); | |||
if (!screen_list->dp) | |||
goto end; | |||
caca_set_cursor(screen_list->dp, 0); | |||
caca_set_display_title(screen_list->dp, PACKAGE_STRING); | |||
while (1) | |||
{ | |||
if (refresh) | |||
{ | |||
caca_set_color_ansi(screen_list->cv, CACA_BLUE, CACA_BLUE); | |||
caca_fill_box(screen_list->cv, | |||
0, 0, | |||
caca_get_canvas_width(screen_list->cv), | |||
caca_get_canvas_height(screen_list->cv), | |||
'#'); | |||
caca_set_color_ansi(screen_list->cv, CACA_DEFAULT, | |||
CACA_BLUE); | |||
caca_draw_cp437_box(screen_list->cv, 0, 0, | |||
caca_get_canvas_width(screen_list->cv), | |||
caca_get_canvas_height(screen_list-> | |||
cv)); | |||
caca_printf(screen_list->cv, 2, 2, | |||
"Please select a session to reconnect:"); | |||
for (i = 0; i < nb_usable_sockets; i++) | |||
{ | |||
if (i == current_line - 1) | |||
{ | |||
caca_set_attr(screen_list->cv, CACA_BOLD); | |||
caca_set_color_ansi(screen_list->cv, CACA_GREEN, | |||
CACA_BLUE); | |||
caca_put_char(screen_list->cv, 1, i + 3, '>'); | |||
} | |||
else | |||
{ | |||
caca_set_attr(screen_list->cv, 0); | |||
caca_set_color_ansi(screen_list->cv, | |||
CACA_LIGHTGRAY, CACA_BLUE); | |||
caca_put_char(screen_list->cv, 1, i + 3, ' '); | |||
} | |||
caca_printf(screen_list->cv, | |||
3, i + 3, | |||
"%s", | |||
socket_to_session(usable_sockets[i])); | |||
} | |||
caca_refresh_display(screen_list->dp); | |||
refresh = 0; | |||
} | |||
if (!caca_get_event(screen_list->dp, | |||
CACA_EVENT_KEY_PRESS | |||
| CACA_EVENT_RESIZE | |||
| CACA_EVENT_QUIT, &ev, 10000)) | |||
continue; | |||
t = caca_get_event_type(&ev); | |||
if (t & CACA_EVENT_KEY_PRESS) | |||
{ | |||
unsigned int c = caca_get_event_key_ch(&ev); | |||
switch (c) | |||
{ | |||
case CACA_KEY_UP: | |||
if (current_line > 1) | |||
current_line--; | |||
break; | |||
case CACA_KEY_DOWN: | |||
if (current_line < nb_usable_sockets) | |||
current_line++; | |||
break; | |||
case CACA_KEY_RETURN: | |||
ret = strdup(usable_sockets[current_line - 1]); | |||
goto end; | |||
break; | |||
case CACA_KEY_ESCAPE: | |||
goto end; | |||
break; | |||
default: | |||
break; | |||
} | |||
refresh = 1; | |||
} | |||
else if (t & CACA_EVENT_RESIZE) | |||
{ | |||
refresh = 1; | |||
} | |||
else if (t & CACA_EVENT_QUIT) | |||
goto end; | |||
} | |||
} | |||
} | |||
end: | |||
if (sockets) | |||
{ | |||
for (i = 0; sockets[i]; i++) | |||
free(sockets[i]); | |||
free(sockets); | |||
} | |||
if (usable_sockets) | |||
{ | |||
for (i = 0; i < nb_usable_sockets; i++) | |||
free(usable_sockets[i]); | |||
free(usable_sockets); | |||
} | |||
if (screen_list->dp) | |||
{ | |||
caca_free_display(screen_list->dp); | |||
screen_list->dp = NULL; | |||
} | |||
if (screen_list->cv) | |||
{ | |||
caca_free_canvas(screen_list->cv); | |||
screen_list->cv = NULL; | |||
} | |||
return ret; | |||
} | |||
void attach(struct screen_list *screen_list) | |||
{ | |||
screen_list->comm.socket_path[SOCK_SERVER] = select_socket(screen_list); | |||
if (screen_list->comm.socket_path[SOCK_SERVER]) | |||
{ | |||
char *session; | |||
session = connect_socket(screen_list, SOCK_SERVER); | |||
if (session) | |||
{ | |||
debug("Connected to session %s", session); | |||
/* Create main canvas and associated caca window */ | |||
screen_list->cv = caca_create_canvas(0, 0); | |||
screen_list->dp = caca_create_display(screen_list->cv); | |||
if (!screen_list->dp) | |||
return; | |||
caca_set_display_time(screen_list->dp, screen_list->delay * 1000); | |||
caca_set_cursor(screen_list->dp, 1); | |||
screen_list->comm.socket_path[SOCK_CLIENT] = | |||
build_socket_path(screen_list->comm.socket_dir, session, | |||
SOCK_CLIENT); | |||
create_socket(screen_list, SOCK_CLIENT); | |||
request_attach(screen_list); | |||
if (screen_list->comm.session_name) | |||
free(screen_list->comm.session_name); | |||
screen_list->comm.session_name = session; | |||
} | |||
else | |||
{ | |||
fprintf(stderr, "Failed to attach!\n"); | |||
free(screen_list->comm.socket_path[SOCK_SERVER]); | |||
screen_list->comm.socket_path[SOCK_SERVER] = NULL; | |||
screen_list->sys.attach = 0; | |||
} | |||
} | |||
else | |||
{ | |||
fprintf(stderr, "No socket found!\n"); | |||
screen_list->sys.attach = 0; | |||
} | |||
} | |||
@@ -0,0 +1,298 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net> | |||
* 2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* 2008-2011 Pascal Terjan <pterjan@linuxfr.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#include "config.h" | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <stdlib.h> | |||
#include <unistd.h> | |||
#include <fcntl.h> | |||
#include <signal.h> | |||
#include <sys/ioctl.h> | |||
#include <sys/types.h> | |||
#include <sys/wait.h> | |||
#include <sys/time.h> | |||
#include <time.h> | |||
#include <pwd.h> | |||
#include <errno.h> | |||
#include <caca.h> | |||
#include "neercs.h" | |||
#define NEERCS_RECV_BUFSIZE 128*1024 | |||
int start_client(struct screen_list *screen_list) | |||
{ | |||
char *sess = NULL; | |||
create_socket(screen_list, SOCK_CLIENT); | |||
while ((sess = connect_socket(screen_list, SOCK_SERVER)) == NULL) | |||
usleep(100); | |||
free(sess); | |||
/* Create main canvas and associated caca window */ | |||
screen_list->cv = caca_create_canvas(0, 0); | |||
screen_list->dp = caca_create_display(screen_list->cv); | |||
screen_list->mouse_button = 0; | |||
if (!screen_list->dp) | |||
return -3; | |||
caca_set_display_time(screen_list->dp, screen_list->delay * 1000); | |||
caca_set_cursor(screen_list->dp, 1); | |||
request_attach(screen_list); | |||
return 0; | |||
} | |||
int send_event(caca_event_t ev, struct screen_list *screen_list) | |||
{ | |||
enum caca_event_type t; | |||
char buf[64]; | |||
int bytes = 0; | |||
t = caca_get_event_type(&ev); | |||
if (t & CACA_EVENT_KEY_PRESS) | |||
{ | |||
bytes = snprintf(buf, sizeof(buf) - 1, "KEY %d", | |||
caca_get_event_key_ch(&ev)); | |||
debug("Sending key press to server: %s", buf); | |||
} | |||
else if (t & CACA_EVENT_RESIZE) | |||
{ | |||
bytes = snprintf(buf, sizeof(buf) - 1, "RESIZE %10d %10d", | |||
caca_get_event_resize_width(&ev), | |||
caca_get_event_resize_height(&ev)); | |||
} | |||
else if (t & CACA_EVENT_MOUSE_PRESS) | |||
{ | |||
screen_list->mouse_button = caca_get_event_mouse_button(&ev); | |||
bytes = snprintf(buf, sizeof(buf) - 1, "MOUSEP %10d %10d %10d", | |||
caca_get_mouse_x(screen_list->dp), | |||
caca_get_mouse_y(screen_list->dp), | |||
screen_list->mouse_button); | |||
} | |||
else if (t & CACA_EVENT_MOUSE_RELEASE) | |||
{ | |||
bytes = snprintf(buf, sizeof(buf) - 1, "MOUSER %10d %10d %10d", | |||
caca_get_mouse_x(screen_list->dp), | |||
caca_get_mouse_y(screen_list->dp), | |||
screen_list->mouse_button); | |||
screen_list->mouse_button = 0; | |||
} | |||
else if (t & CACA_EVENT_MOUSE_MOTION) | |||
{ | |||
int x = caca_get_mouse_x(screen_list->dp); | |||
int y = caca_get_mouse_y(screen_list->dp); | |||
int b = screen_list->mouse_button; | |||
debug("Mouse motion, button %d", b); | |||
if (x != screen_list->old_x || y != screen_list->old_y) | |||
{ | |||
screen_list->old_x = caca_get_mouse_x(screen_list->dp); | |||
screen_list->old_y = caca_get_mouse_y(screen_list->dp); | |||
bytes = snprintf(buf, sizeof(buf) - 1, "MOUSEM %10d %10d %10d", | |||
caca_get_mouse_x(screen_list->dp), | |||
caca_get_mouse_y(screen_list->dp), | |||
b>=0?b:0); | |||
} | |||
} | |||
else if (t & CACA_EVENT_QUIT) | |||
{ | |||
bytes = snprintf(buf, sizeof(buf) - 1, "QUIT"); | |||
} | |||
if (bytes) | |||
{ | |||
ssize_t r; | |||
buf[bytes] = '\0'; | |||
debug("Sending '%s', %d bytes", buf, bytes); | |||
r = write(screen_list->comm.socket[SOCK_SERVER], buf, bytes+1); | |||
while (r < 0 && errno == EAGAIN) | |||
{ | |||
usleep(20); | |||
r = write(screen_list->comm.socket[SOCK_SERVER], buf, bytes+1); | |||
} | |||
return r < 0; | |||
} | |||
return 0; | |||
} | |||
int send_delay(struct screen_list *screen_list) | |||
{ | |||
debug("Sending DELAY\n"); | |||
char buf[18]; | |||
int bytes; | |||
bytes = snprintf(buf, sizeof(buf) - 1, "DELAY %10d", screen_list->delay); | |||
buf[bytes] = '\0'; | |||
return write(screen_list->comm.socket[SOCK_SERVER], buf, strlen(buf)) <= 0; | |||
} | |||
/** \brief Main client loop. | |||
* | |||
* This is the main client loop. | |||
* | |||
* Repeat forever: | |||
* - if data is available on the client socket, read it and interpret it: | |||
* - "DETACH": exit the loop | |||
* - "UPDATE": update screen with the given canvas data | |||
* - "REFRESH": refresh the whole display | |||
* - "CURSOR": set cursor position | |||
* - "TITLE": set window or display title | |||
* - wait for an input event with a 10ms timeout | |||
*/ | |||
void mainloop(struct screen_list *screen_list) | |||
{ | |||
char *buf = NULL; | |||
screen_list->last_key_time = get_us(); | |||
while (mainloop_tick(&buf, screen_list)) | |||
; | |||
free(buf); | |||
} | |||
int mainloop_tick(char **pbuf, struct screen_list *screen_list) | |||
{ | |||
caca_event_t ev; | |||
int ret = 0; | |||
ssize_t n; | |||
if (!screen_list) | |||
return 0; | |||
if (!*pbuf) | |||
*pbuf = malloc(NEERCS_RECV_BUFSIZE); | |||
if (!*pbuf) | |||
{ | |||
debug("Failed to allocate memory"); | |||
return 0; | |||
} | |||
if (screen_list->comm.socket[SOCK_CLIENT] | |||
&& (n = | |||
read(screen_list->comm.socket[SOCK_CLIENT], *pbuf, | |||
NEERCS_RECV_BUFSIZE - 1)) > 0) | |||
{ | |||
*pbuf[n] = 0; | |||
debug("Received from server: '%s' (%d bytes)", *pbuf, n); | |||
if (!strncmp("DETACH", *pbuf, 6)) | |||
{ | |||
/* ret = 1; Not used */ | |||
return 1; | |||
} | |||
else if (!strncmp("UPDATE ", *pbuf, 7)) | |||
{ | |||
int x, y; | |||
ssize_t l2 = 0, lb = 0; | |||
char *buf2; | |||
size_t l = 0; | |||
/* FIXME check the length before calling atoi */ | |||
x = atoi(*pbuf + 8); | |||
y = atoi(*pbuf + 19); | |||
/* 0 means we have valid data but incomplete, so read the rest | |||
*/ | |||
while (l == 0) | |||
{ | |||
buf2 = realloc(*pbuf, l2 + NEERCS_RECV_BUFSIZE); | |||
if (!buf2) | |||
{ | |||
debug("Failed to allocate memory"); | |||
return 0; | |||
} | |||
*pbuf = buf2; | |||
fcntl(screen_list->comm.socket[SOCK_CLIENT], F_SETFL, 0); | |||
lb = read(screen_list->comm.socket[SOCK_CLIENT], *pbuf + l2, | |||
NEERCS_RECV_BUFSIZE - 1); | |||
if (lb < 0) | |||
{ | |||
debug | |||
("Failed to read the end of the refresh message (%s)", | |||
strerror(errno)); | |||
l = -1; | |||
} | |||
else | |||
{ | |||
l2 += lb; | |||
l = caca_import_area_from_memory(screen_list->cv, x, y, | |||
*pbuf, l2, "caca"); | |||
} | |||
} | |||
fcntl(screen_list->comm.socket[SOCK_CLIENT], F_SETFL, | |||
O_NONBLOCK); | |||
} | |||
else if (!strncmp("REFRESH ", *pbuf, 8)) | |||
{ | |||
int dt, x, y; | |||
/* FIXME check the length before calling atoi */ | |||
x = atoi(*pbuf + 8); | |||
y = atoi(*pbuf + 19); | |||
caca_gotoxy(screen_list->cv, x, y); | |||
caca_refresh_display(screen_list->dp); | |||
dt = caca_get_display_time(screen_list->dp); | |||
/* Adjust refresh delay so that the server do not compute | |||
useless things */ | |||
if (dt > 2 * 1000 * screen_list->delay | |||
&& screen_list->delay <= 100) | |||
{ | |||
screen_list->delay *= 2; | |||
send_delay(screen_list); | |||
} | |||
else if (dt < screen_list->delay * 1000 * 1.2 && | |||
screen_list->delay >= | |||
3 * screen_list->requested_delay / 2) | |||
{ | |||
screen_list->delay = 2 * screen_list->delay / 3; | |||
send_delay(screen_list); | |||
} | |||
screen_list->comm.attached = 1; | |||
} | |||
else if (!strncmp("CURSOR ", *pbuf, 7)) | |||
{ | |||
caca_set_cursor(screen_list->dp, atoi(*pbuf + 7)); | |||
} | |||
else if (!strncmp("TITLE ", *pbuf, 6)) | |||
{ | |||
caca_set_display_title(screen_list->dp, *pbuf + 6); | |||
caca_refresh_display(screen_list->dp); | |||
} | |||
else | |||
{ | |||
debug("Unknown message received from server: %s", *pbuf); | |||
} | |||
} | |||
/* Wait to have finished attaching before handling events */ | |||
if (screen_list->comm.attached) | |||
{ | |||
ret = caca_get_event(screen_list->dp, | |||
CACA_EVENT_KEY_PRESS | |||
| CACA_EVENT_MOUSE_PRESS | |||
| CACA_EVENT_MOUSE_RELEASE | |||
| CACA_EVENT_MOUSE_MOTION | |||
| CACA_EVENT_RESIZE | |||
| CACA_EVENT_QUIT, &ev, 10000); | |||
if (ret) | |||
ret = send_event(ev, screen_list); | |||
if (ret) | |||
{ | |||
debug("Failed send event, quitting: %s\n", strerror(errno)); | |||
return 1; | |||
} | |||
} | |||
return 1; | |||
} |
@@ -0,0 +1,568 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net> | |||
* 2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#include <fcntl.h> | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <sys/types.h> | |||
#include <sys/stat.h> | |||
#include <unistd.h> | |||
#include <errno.h> | |||
#include <string.h> | |||
#include "neercs.h" | |||
struct config_line *get_config(const char *name); | |||
int set_window_manager(const char *argv, struct screen_list *screen_list); | |||
int set_cube_duration(const char *argv, struct screen_list *screen_list); | |||
int set_thumbnails(const char *argv, struct screen_list *screen_list); | |||
int set_status_bar(const char *argv, struct screen_list *screen_list); | |||
int set_screensaver_timeout(const char *argv, struct screen_list *screen_list); | |||
int set_autolock_timeout(const char *argv, struct screen_list *screen_list); | |||
int set_lock_on_detach(const char *argv, struct screen_list *screen_list); | |||
int set_socket_dir(const char *argv, struct screen_list *screen_list); | |||
int set_delay(const char *argv, struct screen_list *screen_list); | |||
int set_eyecandy(const char *argv, struct screen_list *screen_list); | |||
int set_border(const char *argv, struct screen_list *screen_list); | |||
char *get_window_manager(struct screen_list *screen_list); | |||
char *get_cube_duration(struct screen_list *screen_list); | |||
char *get_thumbnails(struct screen_list *screen_list); | |||
char *get_status_bar(struct screen_list *screen_list); | |||
char *get_screensaver_timeout(struct screen_list *screen_list); | |||
char *get_autolock_timeout(struct screen_list *screen_list); | |||
char *get_lock_on_detach(struct screen_list *screen_list); | |||
char *get_socket_dir(struct screen_list *screen_list); | |||
char *get_delay(struct screen_list *screen_list); | |||
char *get_eyecandy(struct screen_list *screen_list); | |||
char *get_border(struct screen_list *screen_list); | |||
/* Options definition and associated function pointer */ | |||
struct config_line config_option[] = { | |||
{.name = "window_manager",.set = set_window_manager,.get = | |||
get_window_manager}, | |||
{.name = "eyecandy",.set = set_eyecandy,.get = get_eyecandy}, | |||
{.name = "borders",.set = set_border,.get = get_border}, | |||
{.name = "cube_duration",.set = set_cube_duration,.get = | |||
get_cube_duration}, | |||
{.name = "thumbnails",.set = set_thumbnails,.get = get_thumbnails}, | |||
{.name = "status_bar",.set = set_status_bar,.get = get_status_bar}, | |||
{.name = "screensaver_timeout",.set = set_screensaver_timeout,.get = | |||
get_screensaver_timeout}, | |||
{.name = "autolock_timeout",.set = set_autolock_timeout,.get = | |||
get_autolock_timeout}, | |||
{.name = "lock_on_detach",.set = set_lock_on_detach,.get = | |||
get_lock_on_detach}, | |||
{.name = "socket_dir",.set = set_socket_dir,.get = get_socket_dir}, | |||
{.name = "delay",.set = set_delay,.get = get_delay}, | |||
{.name = "last",.set = NULL}, | |||
}; | |||
int read_configuration_file(char *filename, struct screen_list *screen_list) | |||
{ | |||
FILE *fp; | |||
struct stat st; | |||
int size = 0, i = 0, total = 0, offset = 0, l = 1; | |||
char *buffer = NULL; | |||
screen_list->config = NULL; | |||
/* Check if file exist */ | |||
if (stat(filename, &st) < 0) | |||
{ | |||
return -1; | |||
} | |||
/* Get its size */ | |||
size = st.st_size; | |||
if (!size) | |||
{ | |||
fprintf(stderr, "File too short\n"); | |||
return -1; | |||
} | |||
/* Open it */ | |||
fp = fopen(filename, "r"); | |||
if (!fp) | |||
{ | |||
return -1; | |||
} | |||
buffer = malloc(size + 1); | |||
if (!buffer) | |||
{ | |||
fclose(fp); | |||
fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, | |||
__LINE__); | |||
return -1; | |||
} | |||
/* Read it */ | |||
while ((i = fread(buffer + total, 1, size, fp)) > 0) | |||
{ | |||
total += i; | |||
} | |||
buffer[total] = '\n'; | |||
fclose(fp); | |||
/* Parse it */ | |||
while ((i = | |||
parse_conf_line(buffer + offset, total - offset, screen_list)) > 0) | |||
{ | |||
offset += i; | |||
l++; | |||
} | |||
free(buffer); | |||
/* Fill neercs configuration with it */ | |||
fill_config(screen_list); | |||
return 1; | |||
} | |||
struct config_line *get_config_option(void) | |||
{ | |||
return config_option; | |||
} | |||
int parse_conf_line(char *buf, int size, struct screen_list *screen_list) | |||
{ | |||
int i, s = 0, c = 0; | |||
char *line = NULL; | |||
int l = 0; | |||
int in_quote = 0, end_spaces = 0; | |||
static struct option *prev = NULL; | |||
if (size <= 0) | |||
return -1; | |||
/* Find EOL */ | |||
for (i = 0; i < size; i++) | |||
{ | |||
if (buf[i] == '\n') | |||
{ | |||
s = i + 1; | |||
break; | |||
} | |||
} | |||
/* Strip comments and trailing spaces */ | |||
for (i = 0; i < s; i++) | |||
{ | |||
if (buf[i] == ';' && !in_quote) | |||
{ | |||
break; | |||
} | |||
else if (buf[i] == '\n') | |||
{ | |||
break; | |||
} | |||
else if (buf[i] == ' ' && !c) | |||
{ | |||
} | |||
else | |||
{ | |||
if (line == NULL) | |||
{ | |||
line = malloc(2); | |||
if (!line) | |||
{ | |||
fprintf(stderr, "Can't allocate memory at %s:%d\n", | |||
__FUNCTION__, __LINE__); | |||
return -1; | |||
} | |||
} | |||
else | |||
{ | |||
line = realloc(line, l + 2); | |||
if (!line) | |||
{ | |||
fprintf(stderr, "Can't allocate memory at %s:%d\n", | |||
__FUNCTION__, __LINE__); | |||
return -1; | |||
} | |||
} | |||
if (buf[i] == '"') | |||
in_quote = !in_quote; | |||
if (buf[i] == ' ') | |||
end_spaces++; | |||
else | |||
end_spaces = 0; | |||
line[l] = buf[i]; | |||
line[l + 1] = 0; | |||
l++; | |||
c = 1; | |||
} | |||
} | |||
if (c == 0) | |||
{ | |||
/* This line is empty, do nothing */ | |||
} | |||
else | |||
{ | |||
struct option *option = malloc(sizeof(struct option)); | |||
if (!option) | |||
{ | |||
fprintf(stderr, "Can't allocate memory at %s:%d\n", | |||
__FUNCTION__, __LINE__); | |||
return -1; | |||
} | |||
option->next = NULL; | |||
l -= end_spaces; | |||
line = realloc(line, l + 1); | |||
line[l] = 0; | |||
get_key_value(line, option); | |||
if (!screen_list->config) | |||
screen_list->config = option; | |||
if (prev) | |||
prev->next = option; | |||
prev = option; | |||
} | |||
free(line); | |||
return s; | |||
} | |||
int get_key_value(char *line, struct option *option) | |||
{ | |||
unsigned int i, o = 0, b = 0, end_spaces = 0; | |||
char *cur = NULL; | |||
option->value = NULL; | |||
option->key = NULL; | |||
/* Line is a section delimiter */ | |||
if (line[0] == '[') | |||
{ | |||
option->value = malloc(strlen(line) - 1); | |||
if (!option->value) | |||
{ | |||
fprintf(stderr, "Can't allocate memory at %s:%d\n", | |||
__FUNCTION__, __LINE__); | |||
return -1; | |||
} | |||
memcpy(option->value, line + 1, strlen(line) - 1); | |||
option->value[strlen(line) - 2] = 0; | |||
return 0; | |||
} | |||
cur = malloc(1); | |||
if (!cur) | |||
{ | |||
fprintf(stderr, "Can't allocate memory at %s:%d\n", | |||
__FUNCTION__, __LINE__); | |||
return -1; | |||
} | |||
cur[0] = 0; | |||
for (i = 0; i < strlen(line); i++) | |||
{ | |||
if (line[i] == ' ' && !b) | |||
continue; | |||
if (line[i] == '=') | |||
{ | |||
b = 0; | |||
cur[o - end_spaces] = 0; | |||
cur = realloc(cur, (o - end_spaces) + 1); | |||
if (!cur) | |||
{ | |||
fprintf(stderr, "Can't allocate memory at %s:%d\n", | |||
__FUNCTION__, __LINE__); | |||
return -1; | |||
} | |||
o = 0; | |||
option->key = cur; | |||
cur = malloc(1); | |||
} | |||
else | |||
{ | |||
if (line[i] == ' ') | |||
end_spaces++; | |||
else | |||
end_spaces = 0; | |||
cur = realloc(cur, o + 2); | |||
if (!cur) | |||
{ | |||
fprintf(stderr, "Can't allocate memory at %s:%d\n", | |||
__FUNCTION__, __LINE__); | |||
return -1; | |||
} | |||
cur[o] = line[i]; | |||
o++; | |||
b = 1; | |||
} | |||
} | |||
cur[o] = 0; | |||
option->value = cur; | |||
return 0; | |||
} | |||
struct config_line *get_config(const char *name) | |||
{ | |||
int i = 0; | |||
debug("Looking for '%s'\n", name); | |||
while (strncmp(config_option[i].name, "last", strlen("last"))) | |||
{ | |||
debug("%d Testing against '%s'\n", i, config_option[i].name); | |||
if (!strncmp(name, config_option[i].name, strlen(name))) | |||
{ | |||
debug("Found\n"); | |||
return &config_option[i]; | |||
} | |||
i++; | |||
} | |||
return NULL; | |||
} | |||
int fill_config(struct screen_list *screen_list) | |||
{ | |||
int i = 0; | |||
struct option *option = screen_list->config; | |||
while (option) | |||
{ | |||
if (option->key == NULL) | |||
{ | |||
option = option->next; | |||
continue; | |||
} | |||
struct config_line *c = get_config(option->key); | |||
if (c) | |||
{ | |||
c->set((const char *)option->value, screen_list); | |||
} | |||
option = option->next; | |||
} | |||
return i; | |||
} | |||
/* | |||
* Options setters | |||
*/ | |||
#define IS_OPTION(t) (!strncmp(argv, t, strlen(argv))) | |||
#define IS_OPTION_TRUE (IS_OPTION("true") || IS_OPTION("True") || IS_OPTION("1")) | |||
int set_window_manager(const char *argv, struct screen_list *screen_list) | |||
{ | |||
if (IS_OPTION("full")) | |||
screen_list->wm_type = WM_FULL; | |||
else if (IS_OPTION("hsplit")) | |||
screen_list->wm_type = WM_HSPLIT; | |||
else if (IS_OPTION("vsplit")) | |||
screen_list->wm_type = WM_VSPLIT; | |||
else if (IS_OPTION("card")) | |||
screen_list->wm_type = WM_CARD; | |||
else | |||
{ | |||
fprintf(stderr, "Unknown window manager '%s'\n", argv); | |||
return -1; | |||
} | |||
return 0; | |||
} | |||
int set_cube_duration(const char *argv, struct screen_list *screen_list) | |||
{ | |||
screen_list->cube.duration = atoi(argv) * 1000000; | |||
return 0; | |||
} | |||
int set_thumbnails(const char *argv, struct screen_list *screen_list) | |||
{ | |||
if (IS_OPTION_TRUE) | |||
screen_list->modals.mini = 1; | |||
else | |||
screen_list->modals.mini = 0; | |||
return 0; | |||
} | |||
int set_status_bar(const char *argv, struct screen_list *screen_list) | |||
{ | |||
if (IS_OPTION_TRUE) | |||
screen_list->modals.status = 1; | |||
else | |||
screen_list->modals.status = 0; | |||
return 0; | |||
} | |||
int set_screensaver_timeout(const char *argv, struct screen_list *screen_list) | |||
{ | |||
screen_list->screensaver.timeout = atoi(argv) * 1000000; | |||
/* if timeout is 0, set it to 0xFFFFFFFFFFFFFFFF */ | |||
if (!screen_list->screensaver.timeout) | |||
screen_list->screensaver.timeout -= 1; | |||
return 0; | |||
} | |||
int set_autolock_timeout(const char *argv, struct screen_list *screen_list) | |||
{ | |||
if (screen_list->lock.autolock_timeout == 0 || | |||
screen_list->lock.autolock_timeout == ((long long unsigned int)0) - 1) | |||
{ | |||
screen_list->lock.autolock_timeout = atoi(argv) * 1000000; | |||
/* if timeout is 0, set it to 0xFFFFFFFFFFFFFFFF */ | |||
if (!screen_list->lock.autolock_timeout) | |||
screen_list->lock.autolock_timeout -= 1; | |||
} | |||
return 0; | |||
} | |||
int set_lock_on_detach(const char *argv, struct screen_list *screen_list) | |||
{ | |||
if (IS_OPTION_TRUE) | |||
screen_list->lock.lock_on_detach = 1; | |||
else | |||
screen_list->lock.lock_on_detach = 0; | |||
return 0; | |||
} | |||
int set_eyecandy(const char *argv, struct screen_list *screen_list) | |||
{ | |||
if (IS_OPTION_TRUE) | |||
screen_list->eyecandy = 1; | |||
else | |||
screen_list->eyecandy = 0; | |||
return 0; | |||
} | |||
int set_border(const char *argv, struct screen_list *screen_list) | |||
{ | |||
if (IS_OPTION_TRUE) | |||
screen_list->border_size = 1; | |||
else | |||
screen_list->border_size = 0; | |||
return 0; | |||
} | |||
int set_socket_dir(const char *argv, struct screen_list *screen_list) | |||
{ | |||
screen_list->comm.socket_dir = strdup(argv); | |||
return 0; | |||
} | |||
int set_delay(const char *argv, struct screen_list *screen_list) | |||
{ | |||
screen_list->requested_delay = atoi(argv); | |||
screen_list->delay = atoi(argv); | |||
return 0; | |||
} | |||
char *get_window_manager(struct screen_list *screen_list) | |||
{ | |||
debug("Window manager is %d", screen_list->wm_type); | |||
switch (screen_list->wm_type) | |||
{ | |||
case WM_FULL: | |||
return "full"; | |||
case WM_CARD: | |||
return "card"; | |||
case WM_VSPLIT: | |||
return "vsplit"; | |||
case WM_HSPLIT: | |||
return "hsplit"; | |||
default: | |||
return "invalid window manager"; | |||
} | |||
return NULL; /* Not reached */ | |||
} | |||
char *get_cube_duration(struct screen_list *screen_list) | |||
{ | |||
char *r = malloc(100); | |||
sprintf(r, "%f", (float)screen_list->cube.duration / 1000000.0f); | |||
return r; | |||
} | |||
char *get_thumbnails(struct screen_list *screen_list) | |||
{ | |||
if (screen_list->modals.mini) | |||
return "true"; | |||
return "false"; | |||
} | |||
char *get_status_bar(struct screen_list *screen_list) | |||
{ | |||
if (screen_list->modals.status) | |||
return "true"; | |||
return "false"; | |||
} | |||
char *get_eyecandy(struct screen_list *screen_list) | |||
{ | |||
if (screen_list->eyecandy) | |||
return "true"; | |||
return "false"; | |||
} | |||
char *get_border(struct screen_list *screen_list) | |||
{ | |||
if (screen_list->border_size) | |||
return "true"; | |||
return "false"; | |||
} | |||
char *get_screensaver_timeout(struct screen_list *screen_list) | |||
{ | |||
char *r = malloc(100); | |||
sprintf(r, "%f", (float)screen_list->screensaver.timeout / 1000000.0f); | |||
return r; | |||
} | |||
char *get_autolock_timeout(struct screen_list *screen_list) | |||
{ | |||
char *r = malloc(100); | |||
sprintf(r, "%f", (float)screen_list->lock.autolock_timeout / 1000000.0f); | |||
return r; | |||
} | |||
char *get_lock_on_detach(struct screen_list *screen_list) | |||
{ | |||
if (screen_list->lock.lock_on_detach) | |||
return "true"; | |||
else | |||
return "false"; | |||
} | |||
char *get_socket_dir(struct screen_list *screen_list) | |||
{ | |||
return screen_list->comm.socket_dir; | |||
} | |||
char *get_delay(struct screen_list *screen_list) | |||
{ | |||
char *r = malloc(100); | |||
sprintf(r, "%d", screen_list->requested_delay); | |||
return r; | |||
} |
@@ -0,0 +1,278 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net> | |||
* 2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#include "config.h" | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <time.h> | |||
#include <sys/wait.h> | |||
#include <sys/types.h> | |||
#include <caca.h> | |||
#include "neercs.h" | |||
void draw_thumbnails(struct screen_list *screen_list) | |||
{ | |||
char const *const *fonts; | |||
caca_dither_t *d; | |||
caca_font_t *f; | |||
uint8_t *buf; | |||
int i, y = | |||
caca_get_canvas_height(screen_list->cv) - 6 - screen_list->modals.status; | |||
int miniw, minih; | |||
if (screen_list->count) | |||
{ | |||
fonts = caca_get_font_list(); | |||
f = caca_load_font(fonts[0], 0); | |||
miniw = caca_get_canvas_width(screen_list->screen[0]->cv) | |||
* caca_get_font_width(f); | |||
minih = caca_get_canvas_height(screen_list->screen[0]->cv) | |||
* caca_get_font_height(f); | |||
buf = malloc(4 * miniw * minih); | |||
#if defined(HAVE_ENDIAN_H) | |||
if (__BYTE_ORDER == __BIG_ENDIAN) | |||
#else | |||
/* This is compile-time optimised with at least -O1 or -Os */ | |||
uint32_t const tmp = 0x12345678; | |||
if (*(uint8_t const *)&tmp == 0x12) | |||
#endif | |||
d = caca_create_dither(32, miniw, minih, 4 * miniw, | |||
0xff0000, 0xff00, 0xff, 0x0); | |||
else | |||
d = caca_create_dither(32, miniw, minih, 4 * miniw, | |||
0xff00, 0xff0000, 0xff000000, 0x0); | |||
for (i = 0; i < screen_list->count; i++) | |||
{ | |||
if (!screen_list->screen[i]->changed && !screen_list->changed) | |||
continue; | |||
caca_render_canvas(screen_list->screen[i]->cv, f, buf, | |||
miniw, minih, miniw * 4); | |||
caca_dither_bitmap(screen_list->cv, 20 * i, y, 19, 6, d, buf); | |||
caca_set_color_ansi(screen_list->cv, CACA_WHITE, CACA_BLUE); | |||
if (screen_list->pty == i) | |||
caca_draw_cp437_box(screen_list->cv, 20 * i, y, 19, 6); | |||
caca_printf(screen_list->cv, 20 * i, y, "(%i)", i + 1); | |||
} | |||
caca_free_dither(d); | |||
caca_free_font(f); | |||
free(buf); | |||
} | |||
} | |||
/* FIXME, make this stuff more configurable */ | |||
void draw_status(struct screen_list *screen_list) | |||
{ | |||
int x = 0, y = caca_get_canvas_height(screen_list->cv) - 1; | |||
/* caca_fill_box(screen_list->cv, x, y, | |||
caca_get_canvas_width(screen_list->cv), 1, '#'); */ | |||
/* Hour */ | |||
{ | |||
time_t now = time((time_t *) 0); | |||
struct tm *t = localtime(&now); | |||
char hour[256]; | |||
sprintf(hour, "%02d:%02d", t->tm_hour, t->tm_min); | |||
caca_set_color_ansi(screen_list->cv, CACA_LIGHTBLUE, CACA_BLUE); | |||
caca_printf(screen_list->cv, x, y, "["); | |||
caca_set_color_ansi(screen_list->cv, CACA_DEFAULT, CACA_BLUE); | |||
caca_printf(screen_list->cv, x + 1, y, hour); | |||
caca_set_color_ansi(screen_list->cv, CACA_LIGHTBLUE, CACA_BLUE); | |||
caca_printf(screen_list->cv, x + strlen(hour) + 1, y, "]"); | |||
x += 7; | |||
} | |||
/* Window */ | |||
{ | |||
char text[256]; | |||
sprintf(text, "%d/%d", screen_list->pty + 1, screen_list->count); | |||
caca_set_color_ansi(screen_list->cv, CACA_BLUE, CACA_BLUE); | |||
caca_put_char(screen_list->cv, x, y, '#'); | |||
x++; | |||
caca_set_color_ansi(screen_list->cv, CACA_LIGHTBLUE, CACA_BLUE); | |||
caca_printf(screen_list->cv, x, y, "Window:"); | |||
caca_set_color_ansi(screen_list->cv, CACA_BLUE, CACA_BLUE); | |||
caca_put_char(screen_list->cv, x + 7, y, '#'); | |||
caca_set_color_ansi(screen_list->cv, CACA_DEFAULT, CACA_BLUE); | |||
caca_printf(screen_list->cv, x + 8, y, text); | |||
x += 8 + strlen(text); | |||
} | |||
/* Window Manager */ | |||
{ | |||
char text[256]; | |||
switch (screen_list->wm_type) | |||
{ | |||
case WM_CARD: | |||
strcpy(text, "card"); | |||
break; | |||
case WM_HSPLIT: | |||
strcpy(text, "hsplit"); | |||
break; | |||
case WM_VSPLIT: | |||
strcpy(text, "vsplit"); | |||
break; | |||
case WM_FULL: | |||
default: | |||
strcpy(text, "full"); | |||
break; | |||
} | |||
caca_set_color_ansi(screen_list->cv, CACA_BLUE, CACA_BLUE); | |||
caca_put_char(screen_list->cv, x, y, '#'); | |||
x++; | |||
caca_set_color_ansi(screen_list->cv, CACA_LIGHTBLUE, CACA_BLUE); | |||
caca_printf(screen_list->cv, x, y, "WM:"); | |||
caca_set_color_ansi(screen_list->cv, CACA_BLUE, CACA_BLUE); | |||
caca_put_char(screen_list->cv, x + 3, y, '#'); | |||
caca_set_color_ansi(screen_list->cv, CACA_DEFAULT, CACA_BLUE); | |||
caca_printf(screen_list->cv, x + 4, y, text); | |||
x += 4 + strlen(text); | |||
} | |||
/* Help (must be the last one ) */ | |||
{ | |||
char text[256]; | |||
sprintf(text, "Help: ctrl-a-?"); | |||
caca_set_color_ansi(screen_list->cv, CACA_BLUE, CACA_BLUE); | |||
while (x < | |||
(int)(caca_get_canvas_width(screen_list->cv) - strlen(text))) | |||
{ | |||
caca_put_char(screen_list->cv, x, y, '#'); | |||
x++; | |||
} | |||
caca_set_color_ansi(screen_list->cv, CACA_DEFAULT, CACA_BLUE); | |||
caca_printf(screen_list->cv, x, y, text); | |||
} | |||
} | |||
int update_window_list(int c, struct screen_list *screen_list) | |||
{ | |||
debug("Got %x\n", c); | |||
switch (c) | |||
{ | |||
case 0x111: | |||
if (screen_list->modals.cur_in_list > 0) | |||
screen_list->modals.cur_in_list--; | |||
break; | |||
case 0x112: | |||
if (screen_list->modals.cur_in_list < screen_list->count - 1) | |||
screen_list->modals.cur_in_list++; | |||
break; | |||
case 0xd: | |||
screen_list->modals.window_list = 0; | |||
screen_list->prevpty = screen_list->pty; | |||
screen_list->pty = screen_list->modals.cur_in_list; | |||
break; | |||
case 0x22: | |||
screen_list->modals.window_list = 0; | |||
break; | |||
default: | |||
break; | |||
} | |||
return 1; | |||
} | |||
void draw_list(struct screen_list *screen_list) | |||
{ | |||
int i; | |||
int w = (caca_get_canvas_width(screen_list->cv)); | |||
int h = (caca_get_canvas_height(screen_list->cv)); | |||
debug("Drawing list\n"); | |||
caca_set_color_ansi(screen_list->cv, CACA_BLACK, CACA_BLACK); | |||
caca_fill_box(screen_list->cv, 0, 0, w, h, '#'); | |||
caca_set_color_ansi(screen_list->cv, CACA_DEFAULT, CACA_DEFAULT); | |||
caca_draw_cp437_box(screen_list->cv, 0, 0, w, h); | |||
caca_printf(screen_list->cv, 2, 1, "Num Name"); | |||
for (i = 0; i < screen_list->count; i++) | |||
{ | |||
char line[1024]; | |||
if (screen_list->modals.cur_in_list == i) | |||
caca_set_color_ansi(screen_list->cv, CACA_BLACK, CACA_WHITE); | |||
else | |||
caca_set_color_ansi(screen_list->cv, CACA_DEFAULT, CACA_DEFAULT); | |||
sprintf(line, "%d %s", i + 1, screen_list->screen[i]->title); | |||
caca_printf(screen_list->cv, 2, i + 3, line); | |||
} | |||
} | |||
/* Close a window by animating it collapsing */ | |||
/* Total close time */ | |||
#define DELAY 500000.0f | |||
int close_screen_recurrent(struct screen_list *screen_list, | |||
struct recurrent *rec, void *user, | |||
long long unsigned int t) | |||
{ | |||
long long unsigned int delta = t - rec->start_time; | |||
screen_list->dont_update_coords = 1; | |||
screen_list->delay = 0; | |||
rec->kill_me = 0; | |||
if (delta >= DELAY || (!screen_list->eyecandy)) | |||
{ | |||
rec->kill_me = 1; | |||
remove_screen(screen_list, screen_list->pty, 1); | |||
screen_list->pty = screen_list->prevpty>screen_list->count?screen_list->count:screen_list->prevpty; | |||
screen_list->prevpty = 0; | |||
screen_list->dont_update_coords = 0; | |||
} | |||
else | |||
{ | |||
float r = 1 - ((DELAY - (DELAY - delta)) / DELAY); | |||
caca_canvas_t *old, *new; | |||
struct screen *s = screen_list->screen[screen_list->pty]; | |||
int w = s->orig_w * r; | |||
int h = s->orig_h * r; | |||
/* libcaca canvas resize function is bugged, do it by hand */ | |||
old = s->cv; | |||
new = caca_create_canvas(w, h); | |||
caca_blit(new, 0, 0, old, NULL); | |||
s->cv = new; | |||
caca_free_canvas(old); | |||
set_tty_size(s->fd, w, h); | |||
s->w = w; | |||
s->h = h; | |||
s->x = (s->orig_x * r) + ((s->orig_w / 2) - s->w / 2); | |||
s->y = (s->orig_y * r) + ((s->orig_h / 2) - s->h / 2); | |||
} | |||
screen_list->changed = 1; | |||
return 1; | |||
} |
@@ -0,0 +1,393 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2008-2010 Pascal Terjan <pterjan@linuxfr.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#include "config.h" | |||
#define _XOPEN_SOURCE 500 /* getsid() */ | |||
#include <dirent.h> | |||
#include <errno.h> | |||
#include <fcntl.h> | |||
#include <glob.h> | |||
#include <libgen.h> | |||
#include <signal.h> | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <unistd.h> | |||
#include <sys/types.h> | |||
#include <sys/stat.h> | |||
#include <sys/wait.h> | |||
#if defined HAVE_LINUX_KDEV_T_H | |||
# include <linux/kdev_t.h> | |||
# include <linux/major.h> | |||
#endif | |||
#include "neercs.h" | |||
#include "mytrace.h" | |||
int grab_process(long pid, char *ptyname, int ptyfd, int *newpid) | |||
{ | |||
#if defined HAVE_LINUX_KDEV_T_H | |||
char fdstr[1024]; | |||
struct mytrace *parent, *child; | |||
int i = 0, fd = 0, ret; | |||
char to_open[128]; | |||
int mode[128]; | |||
int fds[128]; | |||
struct stat stat_buf; | |||
struct termios tos; | |||
int validtos = 0; | |||
DIR *fddir; | |||
struct dirent *fddirent; | |||
debug("pty is %s", ptyname); | |||
parent = mytrace_attach(pid); | |||
if (!parent) | |||
{ | |||
fprintf(stderr, "Cannot access process %ld\n", pid); | |||
return -1; | |||
} | |||
child = mytrace_fork(parent); | |||
snprintf(fdstr, sizeof(fdstr), "/proc/%ld/fd", pid); | |||
fddir = opendir(fdstr); | |||
/* Look for file descriptors that are PTYs */ | |||
while ((fddirent = readdir(fddir)) && i < (int)sizeof(to_open) - 1) | |||
{ | |||
fd = atoi(fddirent->d_name); | |||
fds[i] = fd; | |||
to_open[i] = 0; | |||
lstat(fdstr, &stat_buf); | |||
if ((stat_buf.st_mode & S_IRUSR) && (stat_buf.st_mode & S_IWUSR)) | |||
mode[i] = O_RDWR; | |||
else if (stat_buf.st_mode & S_IWUSR) | |||
mode[i] = O_WRONLY; | |||
else | |||
mode[i] = O_RDONLY; | |||
snprintf(fdstr, sizeof(fdstr), "/proc/%ld/fd/%s", pid, | |||
fddirent->d_name); | |||
if (stat(fdstr, &stat_buf) < 0) | |||
continue; | |||
if (!S_ISCHR(stat_buf.st_mode) | |||
|| MAJOR(stat_buf.st_rdev) != UNIX98_PTY_SLAVE_MAJOR) | |||
continue; | |||
debug("found pty %d for pid %d", fd, pid); | |||
if (!validtos) | |||
{ | |||
ret = mytrace_tcgets(child, fd, &tos); | |||
if (ret < 0) | |||
{ | |||
perror("mytrace_tcgets"); | |||
} | |||
else | |||
{ | |||
validtos = 1; | |||
} | |||
} | |||
to_open[i] = 1; | |||
i++; | |||
} | |||
closedir(fddir); | |||
if (i >= (int)sizeof(to_open) - 1) | |||
{ | |||
fprintf(stderr, "too many open pty\n"); | |||
mytrace_detach(child); | |||
return -1; | |||
} | |||
ret = mytrace_exec(parent, "/usr/bin/reset"); | |||
if (ret < 0) | |||
mytrace_exit(parent, 0); | |||
mytrace_detach(parent); | |||
waitpid(pid, NULL, 0); /* Wait for reset to finish before displaying */ | |||
mytrace_write(child, 2, "\033[H\033[2J", 7); | |||
mytrace_write(child, 2, "\n[Process stolen by neercs]\r\n\n", 30); | |||
pid = mytrace_getpid(child); | |||
*newpid = pid; | |||
/* Set the process's session ID */ | |||
debug("Running setsid on process %ld (sid=%d)", pid, getsid(pid)); | |||
ret = mytrace_setpgid(child, 0, getsid(pid)); | |||
if (ret < 0) | |||
{ | |||
fprintf(stderr, "syscall setpgid failed\n"); | |||
mytrace_detach(child); | |||
return -1; | |||
} | |||
if (ret != 0) | |||
{ | |||
fprintf(stderr, "setpgid returned %d\n", ret); | |||
mytrace_detach(child); | |||
return -1; | |||
} | |||
ret = mytrace_setsid(child); | |||
if (ret < 0) | |||
{ | |||
fprintf(stderr, "syscall setsid failed\n"); | |||
mytrace_detach(child); | |||
return -1; | |||
} | |||
debug("pid %ld has now sid %d", pid, getsid(pid)); | |||
/* Reopen PTY file descriptors */ | |||
for (; i >= 0; i--) | |||
{ | |||
if (!to_open[i]) | |||
continue; | |||
ret = mytrace_close(child, fds[i]); | |||
if (ret < 0) | |||
{ | |||
perror("mytrace_close"); | |||
continue; | |||
} | |||
fd = mytrace_open(child, ptyname, mode[i]); | |||
if (fd < 0) | |||
{ | |||
perror("mytrace_open"); | |||
continue; | |||
} | |||
/* FIXME Only needed once */ | |||
mytrace_sctty(child, fd); | |||
if (validtos) | |||
{ | |||
ret = mytrace_tcsets(child, fd, &tos); | |||
if (ret < 0) | |||
{ | |||
perror("mytrace_tcsets"); | |||
} | |||
validtos = 0; | |||
} | |||
ret = mytrace_dup2(child, fd, fds[i]); | |||
if (ret < 0) | |||
{ | |||
perror("mytrace_dup2"); | |||
} | |||
} | |||
kill(pid, SIGWINCH); | |||
mytrace_detach(child); | |||
close(ptyfd); | |||
return 0; | |||
#else | |||
errno = ENOSYS; | |||
return -1; | |||
#endif | |||
} | |||
struct process | |||
{ | |||
long pid; | |||
char *cmdline; | |||
}; | |||
static int list_process(struct process **process_list) | |||
{ | |||
glob_t pglob; | |||
unsigned int i, n = 0; | |||
glob("/proc/[0-9]*", GLOB_NOSORT, NULL, &pglob); | |||
*process_list = malloc(pglob.gl_pathc * sizeof(struct process)); | |||
for (i = 0; i < pglob.gl_pathc; i++) | |||
{ | |||
glob_t pglob2; | |||
unsigned int j; | |||
char *fds; | |||
(*process_list)[n].pid = atoi(basename(pglob.gl_pathv[i])); | |||
/* Don't allow grabbing ourselves */ | |||
if ((*process_list)[n].pid == getpid()) | |||
continue; | |||
/* FIXME check value of r */ | |||
int r = asprintf(&fds, "%s/fd/*", pglob.gl_pathv[i]); | |||
(void) r; | |||
glob(fds, GLOB_NOSORT, NULL, &pglob2); | |||
free(fds); | |||
for (j = 0; j < pglob2.gl_pathc; j++) | |||
{ | |||
char path[4096]; | |||
ssize_t l = readlink(pglob2.gl_pathv[j], path, sizeof(path)); | |||
if (l <= 0) | |||
continue; | |||
path[l] = '\0'; | |||
if (strstr(path, "/dev/pt")) | |||
{ | |||
char *cmdfile; | |||
int fd; | |||
/* FIXME check value of r */ | |||
r = asprintf(&cmdfile, "%s/cmdline", pglob.gl_pathv[i]); | |||
(void) r; | |||
fd = open(cmdfile, O_RDONLY); | |||
free(cmdfile); | |||
if (fd) | |||
{ | |||
/* FIXME check value of r */ | |||
r = read(fd, path, sizeof(path)); | |||
(void) r; | |||
(*process_list)[n].cmdline = strdup(path); | |||
close(fd); | |||
n++; | |||
break; | |||
} | |||
} | |||
} | |||
globfree(&pglob2); | |||
} | |||
globfree(&pglob); | |||
return n; | |||
} | |||
long select_process(struct screen_list *screen_list) | |||
{ | |||
caca_event_t ev; | |||
enum caca_event_type t; | |||
int current_line = 1; | |||
int refresh = 1; | |||
int nb_process, i; | |||
int ret = 0; | |||
int start = 0; | |||
struct process *process_list; | |||
nb_process = list_process(&process_list); | |||
screen_list->cv = caca_create_canvas(0, 0); | |||
screen_list->dp = caca_create_display(screen_list->cv); | |||
if (!screen_list->dp) | |||
goto end; | |||
caca_set_cursor(screen_list->dp, 0); | |||
caca_set_display_title(screen_list->dp, PACKAGE_STRING); | |||
while (1) | |||
{ | |||
if (refresh) | |||
{ | |||
caca_set_color_ansi(screen_list->cv, CACA_BLUE, CACA_BLUE); | |||
caca_fill_box(screen_list->cv, | |||
0, 0, | |||
caca_get_canvas_width(screen_list->cv), | |||
caca_get_canvas_height(screen_list->cv), '#'); | |||
caca_set_color_ansi(screen_list->cv, CACA_DEFAULT, CACA_BLUE); | |||
caca_draw_cp437_box(screen_list->cv, | |||
0, 0, | |||
caca_get_canvas_width(screen_list->cv), | |||
caca_get_canvas_height(screen_list->cv)); | |||
caca_printf(screen_list->cv, 2, 2, | |||
"Please select a process to grab:"); | |||
for (i = 0; | |||
i < nb_process | |||
&& i < caca_get_canvas_height(screen_list->cv) - 4; i++) | |||
{ | |||
if (i == current_line - 1) | |||
{ | |||
caca_set_attr(screen_list->cv, CACA_BOLD); | |||
caca_set_color_ansi(screen_list->cv, CACA_GREEN, | |||
CACA_BLUE); | |||
caca_put_char(screen_list->cv, 1, i + 3, '>'); | |||
} | |||
else | |||
{ | |||
caca_set_attr(screen_list->cv, 0); | |||
caca_set_color_ansi(screen_list->cv, CACA_LIGHTGRAY, | |||
CACA_BLUE); | |||
caca_put_char(screen_list->cv, 1, i + 3, ' '); | |||
} | |||
caca_printf(screen_list->cv, | |||
3, i + 3, | |||
"%5d %s", | |||
process_list[i + start].pid, | |||
process_list[i + start].cmdline); | |||
} | |||
caca_refresh_display(screen_list->dp); | |||
refresh = 0; | |||
} | |||
if (!caca_get_event(screen_list->dp, | |||
CACA_EVENT_KEY_PRESS | |||
| CACA_EVENT_RESIZE | CACA_EVENT_QUIT, &ev, 10000)) | |||
continue; | |||
t = caca_get_event_type(&ev); | |||
if (t & CACA_EVENT_KEY_PRESS) | |||
{ | |||
unsigned int c = caca_get_event_key_ch(&ev); | |||
switch (c) | |||
{ | |||
case CACA_KEY_UP: | |||
if (current_line > 1) | |||
current_line--; | |||
if (current_line < start && start > 0) | |||
start--; | |||
break; | |||
case CACA_KEY_DOWN: | |||
if (current_line < nb_process) | |||
current_line++; | |||
if (current_line > | |||
start + caca_get_canvas_height(screen_list->cv) - 3) | |||
start++; | |||
break; | |||
case CACA_KEY_RETURN: | |||
ret = process_list[current_line - 1].pid; | |||
goto end; | |||
break; | |||
case CACA_KEY_ESCAPE: | |||
goto end; | |||
break; | |||
default: | |||
break; | |||
} | |||
refresh = 1; | |||
} | |||
else if (t & CACA_EVENT_RESIZE) | |||
{ | |||
refresh = 1; | |||
} | |||
else if (t & CACA_EVENT_QUIT) | |||
goto end; | |||
} | |||
end: | |||
if (screen_list->dp) | |||
{ | |||
caca_free_display(screen_list->dp); | |||
screen_list->dp = NULL; | |||
} | |||
if (screen_list->cv) | |||
{ | |||
caca_free_canvas(screen_list->cv); | |||
screen_list->cv = NULL; | |||
} | |||
if (nb_process > 0) | |||
{ | |||
for (i = 0; i < nb_process; i++) | |||
{ | |||
free(process_list[i].cmdline); | |||
} | |||
free(process_list); | |||
} | |||
return ret; | |||
} |
@@ -0,0 +1,76 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net> | |||
* 2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#include "config.h" | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <time.h> | |||
#include <sys/wait.h> | |||
#include <sys/types.h> | |||
#include <caca.h> | |||
#include "neercs.h" | |||
int help_handle_key(struct screen_list *screen_list, unsigned int c) | |||
{ | |||
if (c == CACA_KEY_ESCAPE || c == '?') | |||
{ | |||
screen_list->modals.help = 0; | |||
screen_list->changed = 1; | |||
return 1; | |||
} | |||
else | |||
{ | |||
return 0; | |||
} | |||
} | |||
void draw_help(struct screen_list *screen_list) | |||
{ | |||
int w = 65, h = 20; | |||
int x = (caca_get_canvas_width(screen_list->cv) - w) / 2; | |||
int y = (caca_get_canvas_height(screen_list->cv) - h) / 2; | |||
caca_set_color_ansi(screen_list->cv, CACA_BLUE, CACA_BLUE); | |||
caca_fill_box(screen_list->cv, x, y, w, h, '#'); | |||
caca_set_color_ansi(screen_list->cv, CACA_DEFAULT, CACA_BLUE); | |||
caca_draw_cp437_box(screen_list->cv, x, y, w, h); | |||
x += 2; | |||
y++; | |||
caca_printf(screen_list->cv, | |||
(caca_get_canvas_width(screen_list->cv) - | |||
strlen(PACKAGE_STRING)) / 2, y - 1, PACKAGE_STRING); | |||
caca_printf(screen_list->cv, x, y++, "Copyright (c) 2006-2010"); | |||
caca_printf(screen_list->cv, x, y++, " Sam Hocevar <sam@zoy.org>"); | |||
caca_printf(screen_list->cv, x, y++, " Jean-Yves Lamoureux <jylam@lnxscene.org>"); | |||
caca_printf(screen_list->cv, x, y++, " Pascal Terjan <pterjan@linuxfr.org>"); | |||
caca_printf(screen_list->cv, x, y++, ""); | |||
caca_printf(screen_list->cv, x, y++, ""); | |||
caca_printf(screen_list->cv, x, y++, "All shortcuts are in format 'ctrl-a-X' where X is :"); | |||
caca_printf(screen_list->cv, x, y++, "n: Next window"); | |||
caca_printf(screen_list->cv, x, y++, "p: Previous window"); | |||
caca_printf(screen_list->cv, x, y++, "w: Switch window manager"); | |||
caca_printf(screen_list->cv, x, y++, "c: Create new window"); | |||
caca_printf(screen_list->cv, x, y++, "m: Thumbnails"); | |||
caca_printf(screen_list->cv, x, y++, "d: Detach"); | |||
caca_printf(screen_list->cv, x, y++, "k: Close window and kill associated process"); | |||
caca_printf(screen_list->cv, x, y++, "h: Dump screen into a file"); | |||
caca_printf(screen_list->cv, x, y++, "?: This help"); | |||
caca_printf(screen_list->cv, x, y++, ""); | |||
caca_printf(screen_list->cv, x, y++, ""); | |||
caca_printf(screen_list->cv, x, y, "See http://caca.zoy.org/wiki/neercs for more informations"); | |||
} |
@@ -0,0 +1,192 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net> | |||
* 2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* 2008-2010 Pascal Terjan <pterjan@linuxfr.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#include "config.h" | |||
#include <caca.h> | |||
#include <string.h> | |||
#include "neercs.h" | |||
struct kconv | |||
{ | |||
unsigned int key; | |||
char *val; | |||
int size; | |||
}; | |||
struct kconv kconv[] = { | |||
{CACA_KEY_UP, "\033OA", 3}, | |||
{CACA_KEY_DOWN, "\033OB", 3}, | |||
{CACA_KEY_RIGHT, "\033OC", 3}, | |||
{CACA_KEY_LEFT, "\033OD", 3}, | |||
{CACA_KEY_PAGEUP, "\033[5~", 4}, | |||
{CACA_KEY_PAGEDOWN, "\033[6~", 4}, | |||
{CACA_KEY_HOME, "\033[1~", 4}, | |||
{CACA_KEY_INSERT, "\033[2~", 4}, | |||
{CACA_KEY_DELETE, "\033[3~", 4}, | |||
{CACA_KEY_END, "\033[4~", 4}, | |||
{CACA_KEY_F1, "\033[11~", 5}, | |||
{CACA_KEY_F2, "\033[12~", 5}, | |||
{CACA_KEY_F3, "\033[13~", 5}, | |||
{CACA_KEY_F4, "\033[14~", 5}, | |||
{CACA_KEY_F5, "\033[15~", 5}, | |||
{CACA_KEY_F6, "\033[17~", 5}, | |||
{CACA_KEY_F7, "\033[18~", 5}, | |||
{CACA_KEY_F8, "\033[19~", 5}, | |||
{CACA_KEY_F9, "\033[20~", 5}, | |||
{CACA_KEY_F10, "\033[21~", 5}, | |||
{CACA_KEY_F11, "\033[23~", 5}, | |||
{CACA_KEY_F12, "\033[24~", 5}, | |||
}; | |||
void *convert_input_ansi(unsigned int *c, int *size) | |||
{ | |||
unsigned int i; | |||
for (i = 0; i < sizeof(kconv) / sizeof(struct kconv); i++) | |||
{ | |||
if (*c == kconv[i].key) | |||
{ | |||
*size = kconv[i].size; | |||
return kconv[i].val; | |||
} | |||
} | |||
*size = 1; | |||
return c; | |||
} | |||
int handle_command_input(struct screen_list *screen_list, unsigned int c) | |||
{ | |||
int refresh = 0; | |||
debug("Key %x\n", c); | |||
screen_list->changed = 1; | |||
if (c >= '0' && c <= '9') | |||
{ | |||
int n = c - 49; | |||
if (n < screen_list->count) | |||
{ | |||
screen_list->prevpty = screen_list->pty; | |||
screen_list->pty = n == -1 ? 10 : n; | |||
return 1; | |||
} | |||
else | |||
{ | |||
return 0; | |||
} | |||
} | |||
switch (c) | |||
{ | |||
case 0x01: // CACA_KEY_CTRL_A: | |||
screen_list->pty ^= screen_list->prevpty; | |||
screen_list->prevpty ^= screen_list->pty; | |||
screen_list->pty ^= screen_list->prevpty; | |||
refresh = 1; | |||
break; | |||
case 'm': | |||
case 0x0d: // CACA_KEY_CTRL_M: | |||
screen_list->modals.mini = !screen_list->modals.mini; | |||
refresh = 1; | |||
break; | |||
case 'n': | |||
case ' ': | |||
case '\0': | |||
case 0x0e: // CACA_KEY_CTRL_N: | |||
screen_list->prevpty = screen_list->pty; | |||
screen_list->pty = (screen_list->pty + 1) % screen_list->count; | |||
if (screen_list->pty != screen_list->prevpty) | |||
{ | |||
screen_list->last_switch = get_us(); | |||
screen_list->cube.in_switch = 1; | |||
screen_list->cube.side = 0; | |||
} | |||
refresh = 1; | |||
break; | |||
case 'p': | |||
case 0x10: // CACA_KEY_CTRL_P: | |||
screen_list->prevpty = screen_list->pty; | |||
screen_list->pty = | |||
(screen_list->pty + screen_list->count - 1) % screen_list->count; | |||
if (screen_list->pty != screen_list->prevpty) | |||
{ | |||
screen_list->last_switch = get_us(); | |||
screen_list->cube.in_switch = 1; | |||
screen_list->cube.side = 1; | |||
} | |||
refresh = 1; | |||
break; | |||
case 'c': | |||
case 0x03: // CACA_KEY_CTRL_C: | |||
screen_list->prevpty = screen_list->pty; | |||
screen_list->pty = | |||
add_screen(screen_list, | |||
create_screen(screen_list->width, | |||
screen_list->height, | |||
screen_list->sys.default_shell)); | |||
refresh = 1; | |||
break; | |||
case 'w': | |||
case 0x17: // CACA_KEY_CTRL_W: | |||
screen_list->wm_type = (screen_list->wm_type == (WM_MAX - 1) ? | |||
screen_list->wm_type = 0 : | |||
screen_list->wm_type + 1); | |||
refresh = 1; | |||
break; | |||
case 'k': | |||
case 0x0b: // CACA_KEY_CTRL_K: | |||
add_recurrent(screen_list->recurrent_list, close_screen_recurrent, | |||
screen_list->cv); | |||
refresh = 1; | |||
break; | |||
case 'x': | |||
case 0x18: // CACA_KEY_CTRL_X: | |||
memset(screen_list->lock.lockpass, 0, 1024); | |||
screen_list->lock.locked = 1; | |||
screen_list->lock.lock_offset = 0; | |||
refresh = 1; | |||
break; | |||
case 'h': | |||
case 0x08: // CACA_KEY_CTRL_H: | |||
dump_to_file(screen_list); | |||
break; | |||
case '?': | |||
screen_list->modals.help = !screen_list->modals.help; | |||
refresh = 1; | |||
break; | |||
case '"': | |||
case 0x34: // CTRL+" | |||
screen_list->modals.cur_in_list = screen_list->pty; | |||
screen_list->modals.window_list = !screen_list->modals.window_list; | |||
refresh = 1; | |||
break; | |||
case 'd': | |||
case 0x04: // CACA_KEY_CTRL_D: | |||
detach(screen_list); | |||
break; | |||
#ifdef USE_PYTHON | |||
case 'e': | |||
case 0x05: | |||
debug("py : command is %d, setting to 1 (at %p)\n", screen_list->modals.python_command, &screen_list->modals.python_command); | |||
screen_list->modals.python_command = 1; | |||
refresh = 1; | |||
break; | |||
#endif | |||
} | |||
return refresh; | |||
} |
@@ -0,0 +1,221 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net> | |||
* 2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#include "config.h" | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <caca.h> | |||
#include <time.h> | |||
#include <sys/wait.h> | |||
#include <sys/types.h> | |||
#if defined USE_LOCK | |||
#if defined HAVE_PAM_PAM_MISC_H | |||
# include <pam/pam_appl.h> | |||
# include <pam/pam_misc.h> | |||
#else | |||
# include <security/pam_appl.h> | |||
# include <security/pam_misc.h> | |||
#endif | |||
# include <pwd.h> | |||
#endif | |||
#include "neercs.h" | |||
#if defined USE_LOCK | |||
static int convpam(int num_msg, const struct pam_message **msg, | |||
struct pam_response **resp, void *appdata_ptr); | |||
#endif | |||
int update_lock(int c, struct screen_list *screen_list) | |||
{ | |||
int refresh = 0; | |||
#if defined USE_LOCK | |||
if (!screen_list->lock.locked) | |||
return 0; | |||
if (c == 0x08) // BACKSPACE | |||
{ | |||
if (screen_list->lock.lock_offset) | |||
{ | |||
screen_list->lock.lockpass[screen_list->lock.lock_offset - 1] = 0; | |||
screen_list->lock.lock_offset--; | |||
} | |||
} | |||
else if (c == 0x0d) // RETURN | |||
{ | |||
memset(screen_list->lock.lockmsg, 0, 1024); | |||
if (validate_lock(screen_list, getenv("USER"), screen_list->lock.lockpass)) | |||
{ | |||
memset(screen_list->lock.lockpass, 0, 1024); | |||
screen_list->lock.locked = 0; | |||
screen_list->lock.lock_offset = 0; | |||
refresh = 1; | |||
} | |||
else | |||
{ | |||
memset(screen_list->lock.lockpass, 0, 1024); | |||
screen_list->lock.lock_offset = 0; | |||
refresh = 1; | |||
} | |||
} | |||
else | |||
{ | |||
if (screen_list->lock.lock_offset < 1023) | |||
{ | |||
screen_list->lock.lockpass[screen_list->lock.lock_offset++] = c; | |||
screen_list->lock.lockpass[screen_list->lock.lock_offset] = 0; | |||
} | |||
} | |||
#endif | |||
return refresh; | |||
} | |||
void draw_lock(struct screen_list *screen_list) | |||
{ | |||
#if defined USE_LOCK | |||
unsigned int i; | |||
char buffer[1024]; | |||
caca_canvas_t *cv = screen_list->cv; | |||
gethostname(buffer, sizeof(buffer) - 1); | |||
int w = 65, h = 20; | |||
int x = (caca_get_canvas_width(cv) - w) / 2; | |||
int y = (caca_get_canvas_height(cv) - h) / 2; | |||
caca_set_color_ansi(cv, CACA_BLUE, CACA_BLUE); | |||
caca_fill_box(cv, x, y, w, h, '#'); | |||
caca_set_color_ansi(cv, CACA_DEFAULT, CACA_BLUE); | |||
caca_draw_cp437_box(cv, x, y, w, h); | |||
x += 2; | |||
y++; | |||
caca_printf(cv, | |||
(caca_get_canvas_width(cv) - | |||
strlen(PACKAGE_STRING " locked")) / 2, y - 1, | |||
PACKAGE_STRING " locked"); | |||
caca_printf(cv, x, y++, "Please type in your password for %s@%s :", | |||
getenv("USER"), buffer); | |||
y += 2; | |||
x = (caca_get_canvas_width(cv) / 2) - | |||
((strlen(screen_list->lock.lockpass) / 2) + strlen("Password : ")); | |||
caca_printf(cv, x, y, "Password : "); | |||
x += strlen("Password : "); | |||
for (i = 0; i < strlen(screen_list->lock.lockpass); i++) | |||
{ | |||
caca_put_str(cv, x, y, "*"); | |||
x++; | |||
} | |||
if (strlen(screen_list->lock.lockmsg)) | |||
{ | |||
x = ((caca_get_canvas_width(cv) - w) / 2) + | |||
(strlen(screen_list->lock.lockmsg)); | |||
y += 2; | |||
caca_set_color_ansi(cv, CACA_RED, CACA_BLUE); | |||
caca_printf(cv, x, y, "Error : %s", screen_list->lock.lockmsg); | |||
} | |||
#endif | |||
} | |||
#if defined USE_LOCK | |||
/* FIXME, handle this without assuming this is a password auth */ | |||
static int convpam(int num_msg, const struct pam_message **msg, | |||
struct pam_response **resp, void *appdata_ptr) | |||
{ | |||
struct pam_response *aresp; | |||
int i; | |||
aresp = calloc(num_msg, sizeof(*aresp)); | |||
for (i = 0; i < num_msg; ++i) | |||
{ | |||
switch (msg[i]->msg_style) | |||
{ | |||
case PAM_PROMPT_ECHO_ON: | |||
case PAM_PROMPT_ECHO_OFF: | |||
aresp[i].resp = strdup(appdata_ptr); | |||
aresp[i].resp_retcode = 0; | |||
break; | |||
case PAM_ERROR_MSG: | |||
break; | |||
default: | |||
printf("Unknow message type from PAM\n"); | |||
break; | |||
} | |||
} | |||
*resp = aresp; | |||
return (PAM_SUCCESS); | |||
} | |||
#endif | |||
int validate_lock(struct screen_list *screen_list, char *user, char *pass) | |||
{ | |||
#if USE_LOCK | |||
int ret; | |||
pam_handle_t *pamh = NULL; | |||
char buffer[100]; | |||
const char *service = "neercs"; | |||
struct pam_conv conv = { | |||
convpam, | |||
pass, | |||
}; | |||
ret = pam_start(service, user, &conv, &pamh); | |||
if (ret != PAM_SUCCESS) | |||
return 0; | |||
pam_set_item(pamh, PAM_RUSER, user); | |||
ret = gethostname(buffer, sizeof(buffer) - 1); | |||
if (ret) | |||
{ | |||
perror("failed to look up hostname"); | |||
ret = pam_end(pamh, PAM_ABORT); | |||
sprintf(screen_list->lock.lockmsg, "Can't get hostname"); | |||
pam_end(pamh, PAM_SUCCESS); | |||
return 0; | |||
} | |||
ret = pam_set_item(pamh, PAM_RHOST, buffer); | |||
if (ret != PAM_SUCCESS) | |||
{ | |||
sprintf(screen_list->lock.lockmsg, "Can't set hostname"); | |||
pam_end(pamh, PAM_SUCCESS); | |||
return 0; | |||
} | |||
ret = pam_authenticate(pamh, 0); | |||
if (ret != PAM_SUCCESS) | |||
{ | |||
sprintf(screen_list->lock.lockmsg, "Can't authenticate"); | |||
pam_end(pamh, PAM_SUCCESS); | |||
return 0; | |||
} | |||
ret = pam_end(pamh, PAM_SUCCESS); | |||
#endif | |||
return 1; | |||
} |
@@ -0,0 +1,324 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net> | |||
* 2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* 2008-2010 Pascal Terjan <pterjan@linuxfr.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#include "config.h" | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <stdlib.h> | |||
#include <unistd.h> | |||
#include <fcntl.h> | |||
#include <signal.h> | |||
#include <sys/ioctl.h> | |||
#include <sys/types.h> | |||
#include <sys/wait.h> | |||
#include <sys/time.h> | |||
#include <time.h> | |||
#if !defined HAVE_GETOPT_LONG | |||
# include "mygetopt.h" | |||
#elif defined HAVE_GETOPT_H | |||
# include <getopt.h> | |||
#endif | |||
#if defined HAVE_GETOPT_LONG | |||
# define mygetopt getopt_long | |||
# define myoptind optind | |||
# define myoptarg optarg | |||
# define myoption option | |||
#endif | |||
#include <errno.h> | |||
#include <caca.h> | |||
#include "neercs.h" | |||
void version(void) | |||
{ | |||
printf("%s\n", PACKAGE_STRING); | |||
printf("Copyright (C) 2006, 2008 Sam Hocevar <sam@zoy.org>\n"); | |||
printf | |||
(" Jean-Yves Lamoureux <jylam@lnxscene.org>\n\n"); | |||
printf | |||
("This is free software. You may redistribute copies of it under the\n"); | |||
printf | |||
("terms of the Do What The Fuck You Want To Public License, Version 2\n"); | |||
printf("<http://sam.zoy.org/wtfpl/>.\n"); | |||
printf("There is NO WARRANTY, to the extent permitted by law.\n"); | |||
printf("\n"); | |||
printf | |||
("For more informations, visit http://libcaca.zoy.org/wiki/neercs\n"); | |||
} | |||
void usage(int argc, char **argv) | |||
{ | |||
printf("%s\n", PACKAGE_STRING); | |||
printf("Usage : %s [command1] [command2] ... [commandN]\n", argv[0]); | |||
printf("Example : %s zsh top \n\n", argv[0]); | |||
printf("Options :\n"); | |||
printf("\t--config\t-c <file>\t\tuse given config file\n"); | |||
printf("\t--pid\t\t-P [pid]\t\tgrab process\n"); | |||
printf("\t\t\t-r [session]\t\treattach to a detached neercs\n"); | |||
printf | |||
("\t\t\t-R [session]\t\treattach if possible, otherwise start a new session\n"); | |||
printf("\t\t\t-S <name>\t\tname this session <name> instead of <pid>\n"); | |||
printf("\t--lock-after\t-l [n]\t\t\tlock screen after n seconds\n"); | |||
printf("\t--version\t-v \t\t\tdisplay version and exit\n"); | |||
printf("\t--help\t\t-h \t\t\tthis help\n"); | |||
} | |||
#if 0 | |||
int main(int argc, char **argv) | |||
{ | |||
struct screen_list *screen_list = init_neercs(argc, argv); | |||
if (!screen_list) | |||
return -1; | |||
mainloop(screen_list); | |||
} | |||
#endif | |||
struct screen_list *init_neercs(int argc, char **argv) | |||
{ | |||
struct screen_list *screen_list = NULL; | |||
int args; | |||
int mainret = -1; | |||
screen_list = create_screen_list(); | |||
screen_list->sys.default_shell = getenv("SHELL"); | |||
args = argc - 1; | |||
if (screen_list->sys.default_shell == NULL && args <= 0) | |||
{ | |||
fprintf(stderr, | |||
"Environment variable SHELL not set and no arguments given. kthxbye.\n"); | |||
free_screen_list(screen_list); | |||
return NULL; | |||
} | |||
if (handle_command_line(argc, argv, screen_list) < 0) | |||
{ | |||
free_screen_list(screen_list); | |||
return NULL; | |||
} | |||
/* Read global configuration first */ | |||
read_configuration_file("/etc/neercsrc", screen_list); | |||
/* Then local one */ | |||
if (screen_list->sys.user_path) | |||
{ | |||
read_configuration_file(screen_list->sys.user_path, screen_list); | |||
free(screen_list->sys.user_path); | |||
} | |||
if (screen_list->sys.attach) | |||
{ | |||
if (screen_list->sys.nb_to_grab || screen_list->sys.to_start) | |||
{ | |||
fprintf(stderr, | |||
"-R can not be associated with commands or pids!\n"); | |||
free_screen_list(screen_list); | |||
return NULL; | |||
} | |||
attach(screen_list); | |||
if (screen_list->sys.forceattach && !screen_list->sys.attach) | |||
{ | |||
free_screen_list(screen_list); | |||
return NULL; | |||
} | |||
} | |||
/* Build default session name */ | |||
if (!screen_list->comm.session_name) | |||
{ | |||
char mypid[32]; /* FIXME Compute the length of PID_MAX ? */ | |||
snprintf(mypid, 31, "%d", getpid()); | |||
mypid[31] = '\0'; | |||
screen_list->comm.session_name = strdup(mypid); | |||
if (!screen_list->comm.session_name) | |||
{ | |||
fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, | |||
__LINE__); | |||
free_screen_list(screen_list); | |||
return NULL; | |||
} | |||
} | |||
if (!screen_list->comm.socket_path[SOCK_CLIENT]) | |||
screen_list->comm.socket_path[SOCK_CLIENT] = | |||
build_socket_path(screen_list->comm.socket_dir, | |||
screen_list->comm.session_name, SOCK_CLIENT); | |||
if (!screen_list->comm.socket_path[SOCK_SERVER]) | |||
screen_list->comm.socket_path[SOCK_SERVER] = | |||
build_socket_path(screen_list->comm.socket_dir, | |||
screen_list->comm.session_name, SOCK_SERVER); | |||
/* Fork the server if needed */ | |||
if (!screen_list->sys.attach) | |||
{ | |||
debug("Spawning a new server"); | |||
if (start_server(screen_list)) | |||
{ | |||
free_screen_list(screen_list); | |||
return NULL; | |||
} | |||
if (start_client(screen_list)) | |||
{ | |||
free_screen_list(screen_list); | |||
return NULL; | |||
} | |||
} | |||
return screen_list; | |||
} | |||
int handle_command_line(int argc, char *argv[], | |||
struct screen_list *screen_list) | |||
{ | |||
int s = 0, i; | |||
for (;;) | |||
{ | |||
int option_index = 0; | |||
int pidopt; | |||
static struct myoption long_options[] = { | |||
{"config", 1, NULL, 'c'}, | |||
#if defined USE_GRAB | |||
{"pid", 0, NULL, 'P'}, | |||
#endif | |||
{"lock-after", 1, NULL, 'l'}, | |||
{"help", 0, NULL, 'h'}, | |||
{"version", 0, NULL, 'v'}, | |||
{NULL, 0, NULL, 0}, | |||
}; | |||
#if defined USE_GRAB | |||
int c = | |||
mygetopt(argc, argv, "c:S:R::l::r::P::hv", long_options, | |||
&option_index); | |||
#else | |||
int c = | |||
mygetopt(argc, argv, "c:S:R::l::r::hv", long_options, | |||
&option_index); | |||
#endif | |||
if (c == -1) | |||
break; | |||
switch (c) | |||
{ | |||
case 'c': /* --config */ | |||
if (screen_list->sys.user_path) | |||
free(screen_list->sys.user_path); | |||
screen_list->sys.user_path = strdup(myoptarg); | |||
s += 2; | |||
break; | |||
case 'S': | |||
if (!screen_list->comm.session_name) | |||
screen_list->comm.session_name = strdup(myoptarg); | |||
s += 2; | |||
break; | |||
case 'P': /* --pid */ | |||
if (myoptarg) | |||
{ | |||
pidopt = atoi(myoptarg); | |||
if (pidopt <= 0) | |||
{ | |||
fprintf(stderr, "Invalid pid %d\n", pidopt); | |||
if (screen_list->sys.to_grab) | |||
free(screen_list->sys.to_grab); | |||
return -1; | |||
} | |||
} | |||
else | |||
pidopt = select_process(screen_list); | |||
if (pidopt <= 0) | |||
{ | |||
s += 1; | |||
break; | |||
} | |||
if (!screen_list->sys.to_grab) | |||
{ | |||
/* At most argc-1-s times -P <pid> + final 0 */ | |||
screen_list->sys.to_grab = | |||
(int *)malloc(((argc - 1 - s) / 2 + 1) * sizeof(int)); | |||
if (!screen_list->sys.to_grab) | |||
{ | |||
fprintf(stderr, "Can't allocate memory at %s:%d\n", | |||
__FUNCTION__, __LINE__); | |||
return -1; | |||
} | |||
} | |||
screen_list->sys.to_grab[screen_list->sys.nb_to_grab++] = pidopt; | |||
screen_list->sys.to_grab[screen_list->sys.nb_to_grab] = 0; | |||
s += 2; | |||
break; | |||
case 'l': | |||
screen_list->lock.autolock_timeout = atoi(myoptarg) * 1000000; | |||
if (screen_list->lock.autolock_timeout == 0) | |||
screen_list->lock.autolock_timeout -= 1; | |||
break; | |||
case 'r': | |||
screen_list->sys.forceattach = 1; | |||
case 'R': | |||
if (screen_list->sys.attach) | |||
{ | |||
fprintf(stderr, "Attaching can only be requested once\n"); | |||
return -1; | |||
} | |||
if (myoptarg) | |||
{ | |||
if (screen_list->comm.session_name) | |||
free(screen_list->comm.session_name); | |||
screen_list->comm.session_name = strdup(myoptarg); | |||
s += 1; | |||
} | |||
screen_list->sys.attach = 1; | |||
s += 1; | |||
break; | |||
case 'h': /* --help */ | |||
usage(argc, argv); | |||
return -1; | |||
break; | |||
case 'v': /* --version */ | |||
version(); | |||
return -1; | |||
break; | |||
case -2: | |||
return -1; | |||
default: | |||
fprintf(stderr, "Unknown argument #%d\n", myoptind); | |||
return -1; | |||
break; | |||
} | |||
} | |||
if (s >= 0 && s < argc - 1) | |||
{ | |||
screen_list->sys.to_start = (char **)malloc((argc - s) * sizeof(char *)); | |||
if (!screen_list->sys.to_start) | |||
{ | |||
fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, | |||
__LINE__); | |||
return -1; | |||
} | |||
for (i = 0; i < (argc - 1) - s; i++) | |||
{ | |||
screen_list->sys.to_start[i] = strdup(argv[i + s + 1]); | |||
} | |||
screen_list->sys.to_start[argc - 1 - s] = NULL; | |||
} | |||
return s; | |||
} |
@@ -0,0 +1,94 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2006-2011 Sam Hocevar <sam@hocevar.net> | |||
* 2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* 2008-2010 Pascal Terjan <pterjan@linuxfr.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#if defined HAVE_CONFIG_H | |||
# include "config.h" | |||
#endif | |||
#include <stdio.h> /* BUFSIZ */ | |||
#include <string.h> /* strncmp() */ | |||
#include <caca.h> | |||
#include "mini-neercs.h" | |||
#include "mini-socket.h" | |||
static caca_display_t *dp; | |||
static caca_canvas_t *cv; | |||
static nrx_socket_t *insock, *outsock; | |||
void client_init(void) | |||
{ | |||
int i, usec = 10000; | |||
cv = caca_create_canvas(0, 0); | |||
dp = caca_create_display(cv); | |||
caca_set_display_title(dp, "Press Esc to quit"); | |||
insock = socket_open("/tmp/neercs.sock.client", 1); | |||
for (i = 0; i < 10; i++) | |||
{ | |||
outsock = socket_open("/tmp/neercs.sock", 0); | |||
if (outsock) | |||
break; | |||
usleep(usec); | |||
usec += usec; | |||
} | |||
socket_puts(outsock, "CONNECT /tmp/neercs.sock.client"); | |||
} | |||
int client_step(void) | |||
{ | |||
caca_event_t ev; | |||
int ret; | |||
/* Handle client sockets */ | |||
ret = socket_select(insock, 1000); | |||
if (ret > 0) | |||
{ | |||
char buf[BUFSIZ]; | |||
ssize_t bytes = socket_read(insock, buf, BUFSIZ); | |||
if (bytes <= 0) | |||
return 1; | |||
/* Parse message */ | |||
if (!strncmp(buf, "OK", strlen("OK"))) | |||
{ | |||
fprintf(stderr, "neercs: connection established\n"); | |||
socket_puts(insock, "TEST insock"); | |||
} | |||
} | |||
/* Handle libcaca events */ | |||
if(caca_get_event(dp, CACA_EVENT_KEY_PRESS, &ev, 1000) | |||
&& caca_get_event_key_ch(&ev) == CACA_KEY_ESCAPE) | |||
return 0; | |||
return 1; | |||
} | |||
void client_fini(void) | |||
{ | |||
socket_puts(outsock, "QUIT /tmp/neercs.sock.client"); | |||
caca_free_display(dp); | |||
caca_free_canvas(cv); | |||
if (insock) | |||
socket_close(insock); | |||
if (outsock) | |||
socket_close(outsock); | |||
} | |||
@@ -0,0 +1,52 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net> | |||
* 2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* 2008-2010 Pascal Terjan <pterjan@linuxfr.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#if defined HAVE_CONFIG_H | |||
# include "config.h" | |||
#endif | |||
#include <stdlib.h> | |||
#include <stdio.h> /* perror() */ | |||
#include <unistd.h> /* fork() */ | |||
#include "mini-neercs.h" | |||
int main(void) | |||
{ | |||
pid_t pid; | |||
pid = fork(); | |||
if (pid < 0) | |||
{ | |||
perror("fork"); | |||
return EXIT_FAILURE; | |||
} | |||
if (pid > 0) | |||
{ | |||
client_init(); | |||
while(client_step()) ; | |||
client_fini(); | |||
} | |||
else | |||
{ | |||
server_init(); | |||
while(server_step()) ; | |||
server_fini(); | |||
} | |||
return EXIT_SUCCESS; | |||
} | |||
@@ -0,0 +1,22 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net> | |||
* 2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* 2008-2010 Pascal Terjan <pterjan@linuxfr.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
void client_init(void); | |||
int client_step(void); | |||
void client_fini(void); | |||
void server_init(void); | |||
int server_step(void); | |||
void server_fini(void); | |||
@@ -0,0 +1,80 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2006-2011 Sam Hocevar <sam@hocevar.net> | |||
* 2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* 2008-2010 Pascal Terjan <pterjan@linuxfr.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#if defined HAVE_CONFIG_H | |||
# include "config.h" | |||
#endif | |||
#include <stdio.h> /* BUFSIZ */ | |||
#include <string.h> /* strncmp() */ | |||
#include "mini-neercs.h" | |||
#include "mini-socket.h" | |||
static nrx_socket_t *insock, *outsock; | |||
void server_init(void) | |||
{ | |||
while (!insock) | |||
insock = socket_open("/tmp/neercs.sock", 1); | |||
} | |||
int server_step(void) | |||
{ | |||
char buf[BUFSIZ]; | |||
ssize_t bytes; | |||
int ret; | |||
if (outsock) | |||
{ | |||
ret = socket_select(outsock, 1000); | |||
if (ret <= 0) | |||
goto nothing; | |||
bytes = socket_read(outsock, buf, BUFSIZ); | |||
if (bytes <= 0) | |||
goto nothing; | |||
} | |||
nothing: | |||
ret = socket_select(insock, 1000); | |||
if (ret <= 0) | |||
return 1; | |||
bytes = socket_read(insock, buf, BUFSIZ); | |||
if (bytes <= 0) | |||
return 1; | |||
/* Parse message */ | |||
if (!strncmp(buf, "CONNECT ", strlen("CONNECT "))) | |||
{ | |||
outsock = socket_open(buf + strlen("CONNECT "), 0); | |||
socket_puts(outsock, "OK"); | |||
} | |||
else if (!strncmp(buf, "QUIT ", strlen("QUIT "))) | |||
{ | |||
return 0; | |||
} | |||
return 1; | |||
} | |||
void server_fini(void) | |||
{ | |||
if (insock) | |||
socket_close(insock); | |||
if (outsock) | |||
socket_close(outsock); | |||
} | |||
@@ -0,0 +1,297 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2006-2011 Sam Hocevar <sam@hocevar.net> | |||
* 2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* 2008-2010 Pascal Terjan <pterjan@linuxfr.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#if defined HAVE_CONFIG_H | |||
# include "config.h" | |||
#endif | |||
#include <stdio.h> /* perror() */ | |||
#include <stdlib.h> /* malloc(), free() */ | |||
#include <unistd.h> /* unlink() */ | |||
#include <fcntl.h> /* fcntl() */ | |||
#include <string.h> /* memcpy() */ | |||
#include <sys/select.h> /* select() */ | |||
#include <sys/types.h> /* bind(), connect() */ | |||
#include <sys/socket.h> /* bind(), connect() */ | |||
#include <sys/stat.h> /* stat(), struct stat */ | |||
#include <sys/un.h> /* AF_UNIX */ | |||
#include <errno.h> /* AF_UNIX */ | |||
#include <time.h> /* time */ | |||
#include "mini-neercs.h" | |||
#include "mini-socket.h" | |||
#define SIZEOF_SUN_PATH (sizeof(((struct sockaddr_un *)NULL)->sun_path)) | |||
#define offsetof(s, f) ((int)(intptr_t)((s *)NULL)->f) | |||
struct nrx_socket | |||
{ | |||
#if 1 | |||
/* Linux sockets */ | |||
int fd; | |||
int server; | |||
int connected; | |||
char path[SIZEOF_SUN_PATH]; | |||
#else | |||
# error No socket implementation | |||
#endif | |||
}; | |||
#define QLEN 10 | |||
int | |||
serv_listen(const char *name) | |||
{ | |||
int fd, len, err, rval; | |||
struct sockaddr_un un; | |||
/* create a UNIX domain stream socket */ | |||
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) | |||
return(-1); | |||
unlink(name); /* in case it already exists */ | |||
/* fill in socket address structure */ | |||
memset(&un, 0, sizeof(un)); | |||
un.sun_family = AF_UNIX; | |||
strcpy(un.sun_path, name); | |||
len = offsetof(struct sockaddr_un, sun_path) + strlen(name); | |||
/* bind the name to the descriptor */ | |||
if (bind(fd, (struct sockaddr *)&un, len) < 0) { | |||
rval = -2; | |||
goto errout; | |||
} | |||
if (listen(fd, QLEN) < 0) { /* tell kernel we're a server */ | |||
rval = -3; | |||
goto errout; | |||
} | |||
return(fd); | |||
errout: | |||
err = errno; | |||
close(fd); | |||
errno = err; | |||
return(rval); | |||
} | |||
#define STALE 30 /* client's name can't be older than this (sec) */ | |||
/* | |||
* Wait for a client connection to arrive, and accept it. | |||
* We also obtain the client's user ID from the pathname | |||
* that it must bind before calling us. | |||
* Returns new fd if all OK, <0 on error | |||
*/ | |||
int | |||
serv_accept(int listenfd, uid_t *uidptr) | |||
{ | |||
int clifd, len, err, rval; | |||
time_t staletime; | |||
struct sockaddr_un un; | |||
struct stat statbuf; | |||
len = sizeof(un); | |||
if ((clifd = accept(listenfd, (struct sockaddr *)&un, &len)) < 0) | |||
return(-1); /* often errno=EINTR, if signal caught */ | |||
/* obtain the client's uid from its calling address */ | |||
len -= offsetof(struct sockaddr_un, sun_path); /* len of pathname */ | |||
un.sun_path[len] = 0; /* null terminate */ | |||
if (stat(un.sun_path, &statbuf) < 0) { | |||
rval = -2; | |||
goto errout; | |||
} | |||
#ifdef S_ISSOCK /* not defined for SVR4 */ | |||
if (S_ISSOCK(statbuf.st_mode) == 0) { | |||
rval = -3; /* not a socket */ | |||
goto errout; | |||
} | |||
#endif | |||
if ((statbuf.st_mode & (S_IRWXG | S_IRWXO)) || | |||
(statbuf.st_mode & S_IRWXU) != S_IRWXU) { | |||
rval = -4; /* is not rwx------ */ | |||
goto errout; | |||
} | |||
staletime = time(NULL) - STALE; | |||
if (statbuf.st_atime < staletime || | |||
statbuf.st_ctime < staletime || | |||
statbuf.st_mtime < staletime) { | |||
rval = -5; /* i-node is too old */ | |||
goto errout; | |||
} | |||
if (uidptr != NULL) | |||
*uidptr = statbuf.st_uid; /* return uid of caller */ | |||
unlink(un.sun_path); /* we're done with pathname now */ | |||
return(clifd); | |||
errout: | |||
err = errno; | |||
close(clifd); | |||
errno = err; | |||
return(rval); | |||
} | |||
#define CLI_PATH "/var/tmp/" /* +5 for pid = 14 chars */ | |||
#define CLI_PERM S_IRWXU /* rwx for user only */ | |||
/* | |||
* Create a client endpoint and connect to a server. | |||
* Returns fd if all OK, <0 on error. | |||
*/ | |||
int | |||
cli_conn(const char *name) | |||
{ | |||
int fd, len, err, rval; | |||
struct sockaddr_un un; | |||
/* create a UNIX domain stream socket */ | |||
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) | |||
return(-1); | |||
/* fill socket address structure with our address */ | |||
memset(&un, 0, sizeof(un)); | |||
un.sun_family = AF_UNIX; | |||
sprintf(un.sun_path, "%s%05d", CLI_PATH, getpid()); | |||
len = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path); | |||
unlink(un.sun_path); /* in case it already exists */ | |||
if (bind(fd, (struct sockaddr *)&un, len) < 0) { | |||
rval = -2; | |||
goto errout; | |||
} | |||
if (chmod(un.sun_path, CLI_PERM) < 0) { | |||
rval = -3; | |||
goto errout; | |||
} | |||
/* fill socket address structure with server's address */ | |||
memset(&un, 0, sizeof(un)); | |||
un.sun_family = AF_UNIX; | |||
strcpy(un.sun_path, name); | |||
len = offsetof(struct sockaddr_un, sun_path) + strlen(name); | |||
if (connect(fd, (struct sockaddr *)&un, len) < 0) { | |||
rval = -4; | |||
goto errout; | |||
} | |||
return(fd); | |||
errout: | |||
err = errno; | |||
close(fd); | |||
errno = err; | |||
return(rval); | |||
} | |||
nrx_socket_t * socket_open(char const *path, int server) | |||
{ | |||
nrx_socket_t * sock; | |||
struct sockaddr_un addr; | |||
int ret, fd; | |||
#if 0 | |||
fd = socket(AF_UNIX, SOCK_STREAM, 0); | |||
//fd = socket(AF_UNIX, SOCK_DGRAM, 0); | |||
if (fd < 0) | |||
{ | |||
perror("socket creation"); | |||
return NULL; | |||
} | |||
memset(&addr, 0, sizeof(struct sockaddr_un)); | |||
addr.sun_family = AF_UNIX; | |||
strncpy(addr.sun_path, path, SIZEOF_SUN_PATH - 1); | |||
if (server) | |||
{ | |||
unlink(path); | |||
ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); | |||
} | |||
else | |||
{ | |||
ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); | |||
} | |||
if (ret < 0) | |||
{ | |||
perror(server ? "socket binding" : "socket connection"); | |||
close(fd); | |||
return NULL; | |||
} | |||
fcntl(fd, F_SETFL, O_NONBLOCK); | |||
#endif | |||
if (server) | |||
fd = serv_listen(path); | |||
else | |||
fd = cli_conn(path); | |||
if (fd < 0) return NULL; | |||
sock = malloc(sizeof(*sock)); | |||
sock->fd = fd; | |||
sock->server = server; | |||
sock->connected = 0; | |||
strncpy(sock->path, path, SIZEOF_SUN_PATH - 1); | |||
return sock; | |||
} | |||
int socket_select(nrx_socket_t *sock, int usecs) | |||
{ | |||
fd_set rfds; | |||
struct timeval tv; | |||
int ret; | |||
FD_ZERO(&rfds); | |||
FD_SET(sock->fd, &rfds); | |||
tv.tv_sec = usecs / 1000000; | |||
tv.tv_usec = usecs % 1000000; | |||
ret = select(sock->fd + 1, &rfds, NULL, NULL, &tv); | |||
if (ret < 0) | |||
return -1; | |||
if (FD_ISSET(sock->fd, &rfds)) | |||
return 1; | |||
return 0; | |||
} | |||
int socket_puts(nrx_socket_t *sock, char const *str) | |||
{ | |||
int ret; | |||
fprintf(stderr, "pid %i sending %i bytes on %s: %s\n", getpid(), (int)strlen(str), sock->path, str); | |||
ret = write(sock->fd, str, strlen(str)); | |||
return ret; | |||
} | |||
ssize_t socket_read(nrx_socket_t *sock, void *buf, size_t count) | |||
{ | |||
int ret; | |||
ret = read(sock->fd, buf, count); | |||
if (ret >= 0) ((char *)buf)[ret] = 0; | |||
if (ret >= 0) fprintf(stderr, "pid %i recving %i bytes on %s: %s\n", getpid(), ret, sock->path, (char *)buf); | |||
return ret; | |||
} | |||
void socket_close(nrx_socket_t *sock) | |||
{ | |||
close(sock->fd); | |||
if (sock->server) | |||
unlink(sock->path); | |||
free(sock); | |||
} | |||
@@ -0,0 +1,24 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net> | |||
* 2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* 2008-2010 Pascal Terjan <pterjan@linuxfr.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#include <sys/types.h> | |||
typedef struct nrx_socket nrx_socket_t; | |||
nrx_socket_t * socket_open(char const *path, int server); | |||
int socket_select(nrx_socket_t *sock, int usecs); | |||
int socket_puts(nrx_socket_t *sock, char const *str); | |||
ssize_t socket_read(nrx_socket_t *sock, void *buf, size_t count); | |||
void socket_close(nrx_socket_t *socket); | |||
@@ -0,0 +1,125 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net> | |||
* 2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
/* | |||
* mygetopt.c: getopt_long reimplementation | |||
*/ | |||
#include "config.h" | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include "caca_types.h" | |||
#include "mygetopt.h" | |||
int myoptind = 1; | |||
char *myoptarg = NULL; | |||
/* XXX: this getopt_long implementation should not be trusted for other | |||
applications without any serious peer reviewing. It “just works” with | |||
zzuf but may fail miserably in other programs. */ | |||
int mygetopt(int argc, char *const _argv[], const char *optstring, | |||
const struct myoption *longopts, int *longindex) | |||
{ | |||
char **argv = (char **)(uintptr_t) _argv; | |||
char *flag; | |||
int i; | |||
if (myoptind >= argc) | |||
return -1; | |||
flag = argv[myoptind]; | |||
if (flag[0] == '-' && flag[1] != '-') | |||
{ | |||
char *tmp; | |||
int ret = flag[1]; | |||
if (ret == '\0') | |||
return -1; | |||
tmp = strchr(optstring, ret); | |||
if (!tmp || ret == ':') | |||
return '?'; | |||
myoptind++; | |||
if (tmp[1] == ':') | |||
{ | |||
if (flag[2] != '\0') | |||
myoptarg = flag + 2; | |||
else if (myoptind >= argc) | |||
{ | |||
if (tmp[2] != ':') | |||
{ | |||
fprintf(stderr, "%s: `%s' needs an argument\n", argv[0], | |||
flag); | |||
return -2; | |||
} | |||
} | |||
else | |||
myoptarg = argv[myoptind++]; | |||
return ret; | |||
} | |||
if (flag[2] != '\0') | |||
{ | |||
flag[1] = '-'; | |||
myoptind--; | |||
argv[myoptind]++; | |||
} | |||
return ret; | |||
} | |||
if (flag[0] == '-' && flag[1] == '-') | |||
{ | |||
if (flag[2] == '\0') | |||
return -1; | |||
for (i = 0; longopts[i].name; i++) | |||
{ | |||
size_t l = strlen(longopts[i].name); | |||
if (strncmp(flag + 2, longopts[i].name, l)) | |||
continue; | |||
switch (flag[2 + l]) | |||
{ | |||
case '=': | |||
if (!longopts[i].has_arg) | |||
goto bad_opt; | |||
if (longindex) | |||
*longindex = i; | |||
myoptind++; | |||
myoptarg = flag + 2 + l + 1; | |||
return longopts[i].val; | |||
case '\0': | |||
if (longindex) | |||
*longindex = i; | |||
myoptind++; | |||
if (longopts[i].has_arg) | |||
myoptarg = argv[myoptind++]; | |||
return longopts[i].val; | |||
default: | |||
break; | |||
} | |||
} | |||
bad_opt: | |||
fprintf(stderr, "%s: unrecognized option `%s'\n", argv[0], flag); | |||
return '?'; | |||
} | |||
return -1; | |||
} |
@@ -0,0 +1,30 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net> | |||
* 2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
/* | |||
* mygetopt.h: getopt_long reimplementation | |||
*/ | |||
struct myoption | |||
{ | |||
const char *name; | |||
int has_arg; | |||
int *flag; | |||
int val; | |||
}; | |||
extern int myoptind; | |||
extern char *myoptarg; | |||
int mygetopt(int, char * const[], const char *, const struct myoption *, int *); | |||
@@ -0,0 +1,785 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2008-2010 Pascal Terjan <pterjan@linuxfr.org> | |||
* 2008-2010 Sam Hocevar <sam@hocevar.net> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#include "config.h" | |||
#include <errno.h> | |||
#include <fcntl.h> | |||
#include <limits.h> | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#if defined USE_GRAB | |||
# include <sys/ioctl.h> | |||
# include <sys/ptrace.h> | |||
# include <sys/stat.h> | |||
# include <sys/syscall.h> | |||
# include <sys/user.h> | |||
# include <sys/wait.h> | |||
#endif | |||
#include "neercs.h" | |||
#include "mytrace.h" | |||
#if defined USE_GRAB | |||
static int memcpy_from_target(struct mytrace *t, | |||
char *dest, long src, size_t n); | |||
static int memcpy_into_target(struct mytrace *t, | |||
long dest, char const *src, size_t n); | |||
static long remote_syscall(struct mytrace *t, long call, | |||
long arg1, long arg2, long arg3); | |||
# if defined DEBUG | |||
static void print_registers(pid_t pid); | |||
# else | |||
# define print_registers(x) do {} while(0) | |||
# endif | |||
#define X(x) #x | |||
#define STRINGIFY(x) X(x) | |||
#define SYSCALL_X86 0x80cd /* CD 80 = int $0x80 */ | |||
#define SYSCALL_X86_NEW 0xf3eb /* EB F3 = jmp <__kernel_vsyscall+0x3> */ | |||
#define SYSENTER 0x340f /* 0F 34 = sysenter */ | |||
#define SYSCALL_AMD64 0x050fL /* 0F 05 = syscall */ | |||
#if defined __x86_64__ | |||
# define RAX rax | |||
# define RBX rbx | |||
# define RCX rcx | |||
# define RDX rdx | |||
# define RSP rsp | |||
# define RBP rbp | |||
# define RIP rip | |||
# define RDI rdi | |||
# define RSI rsi | |||
# define FMT "%016lx" | |||
#else | |||
# define RAX eax | |||
# define RBX ebx | |||
# define RCX ecx | |||
# define RDX edx | |||
# define RSP esp | |||
# define RBP ebp | |||
# define RIP eip | |||
# define RDI edi | |||
# define RSI esi | |||
# define FMT "%08lx" | |||
#endif | |||
#define MYCALL_OPEN 0 | |||
#define MYCALL_CLOSE 1 | |||
#define MYCALL_WRITE 2 | |||
#define MYCALL_DUP2 3 | |||
#define MYCALL_SETPGID 4 | |||
#define MYCALL_SETSID 5 | |||
#define MYCALL_KILL 6 | |||
#define MYCALL_FORK 7 | |||
#define MYCALL_EXIT 8 | |||
#define MYCALL_EXECVE 9 | |||
#define MYCALL_IOCTL 10 | |||
#if defined __x86_64__ | |||
/* from unistd_32.h on an amd64 system */ | |||
int syscalls32[] = { 5, 6, 4, 63, 57, 66, 37, 2, 1, 11, 54 }; | |||
int syscalls64[] = | |||
#else | |||
int syscalls32[] = | |||
#endif | |||
{ SYS_open, SYS_close, SYS_write, SYS_dup2, SYS_setpgid, SYS_setsid, | |||
SYS_kill, SYS_fork, SYS_exit, SYS_execve, SYS_ioctl | |||
}; | |||
char const *syscallnames[] = | |||
{ "open", "close", "write", "dup2", "setpgid", "setsid", "kill", "fork", | |||
"exit", "execve", "ioctl" | |||
}; | |||
#endif /* USE_GRAB */ | |||
struct mytrace | |||
{ | |||
pid_t pid, child; | |||
}; | |||
struct mytrace *mytrace_attach(long int pid) | |||
{ | |||
#if defined USE_GRAB | |||
struct mytrace *t; | |||
int status; | |||
if (ptrace(PTRACE_ATTACH, pid, 0, 0) < 0) | |||
{ | |||
perror("PTRACE_ATTACH (attach)"); | |||
return NULL; | |||
} | |||
if (waitpid(pid, &status, 0) < 0) | |||
{ | |||
perror("waitpid"); | |||
return NULL; | |||
} | |||
if (!WIFSTOPPED(status)) | |||
{ | |||
fprintf(stderr, "traced process was not stopped\n"); | |||
ptrace(PTRACE_DETACH, pid, 0, 0); | |||
return NULL; | |||
} | |||
t = malloc(sizeof(struct mytrace)); | |||
t->pid = pid; | |||
t->child = 0; | |||
return t; | |||
#else | |||
errno = ENOSYS; | |||
return NULL; | |||
#endif | |||
} | |||
struct mytrace *mytrace_fork(struct mytrace *t) | |||
{ | |||
#if defined USE_GRAB | |||
struct mytrace *child; | |||
ptrace(PTRACE_SETOPTIONS, t->pid, NULL, PTRACE_O_TRACEFORK); | |||
remote_syscall(t, MYCALL_FORK, 0, 0, 0); | |||
waitpid(t->child, NULL, 0); | |||
child = malloc(sizeof(struct mytrace)); | |||
child->pid = t->child; | |||
child->child = 0; | |||
return child; | |||
#else | |||
errno = ENOSYS; | |||
return NULL; | |||
#endif | |||
} | |||
int mytrace_detach(struct mytrace *t) | |||
{ | |||
#if defined USE_GRAB | |||
ptrace(PTRACE_DETACH, t->pid, 0, 0); | |||
free(t); | |||
return 0; | |||
#else | |||
errno = ENOSYS; | |||
return -1; | |||
#endif | |||
} | |||
long mytrace_getpid(struct mytrace *t) | |||
{ | |||
#if defined USE_GRAB | |||
return t->pid; | |||
#else | |||
errno = ENOSYS; | |||
return -1; | |||
#endif | |||
} | |||
int mytrace_open(struct mytrace *t, char const *path, int mode) | |||
{ | |||
#if defined USE_GRAB | |||
char backup_data[4096]; | |||
struct user_regs_struct regs; | |||
size_t size = strlen(path) + 1; | |||
int ret, err; | |||
if (ptrace(PTRACE_GETREGS, t->pid, NULL, ®s) < 0) | |||
{ | |||
perror("PTRACE_GETREGS (open)\n"); | |||
return -1; | |||
} | |||
/* Backup the data that we will use */ | |||
if (memcpy_from_target(t, backup_data, regs.RSP, size) < 0) | |||
return -1; | |||
memcpy_into_target(t, regs.RSP, path, size); | |||
ret = remote_syscall(t, MYCALL_OPEN, regs.RSP, O_RDWR, 0755); | |||
err = errno; | |||
/* Restore the data */ | |||
memcpy_into_target(t, regs.RSP, backup_data, size); | |||
errno = err; | |||
return ret; | |||
#else | |||
errno = ENOSYS; | |||
return -1; | |||
#endif | |||
} | |||
int mytrace_close(struct mytrace *t, int fd) | |||
{ | |||
#if defined USE_GRAB | |||
return remote_syscall(t, MYCALL_CLOSE, fd, 0, 0); | |||
#else | |||
errno = ENOSYS; | |||
return -1; | |||
#endif | |||
} | |||
int mytrace_write(struct mytrace *t, int fd, char const *data, size_t len) | |||
{ | |||
#if defined USE_GRAB | |||
struct user_regs_struct regs; | |||
char *backup_data; | |||
int ret, err; | |||
if (ptrace(PTRACE_GETREGS, t->pid, NULL, ®s) < 0) | |||
{ | |||
perror("PTRACE_GETREGS (write)\n"); | |||
return -1; | |||
} | |||
backup_data = malloc(len); | |||
/* Backup the data that we will use */ | |||
if (memcpy_from_target(t, backup_data, regs.RSP, len) < 0) | |||
return -1; | |||
memcpy_into_target(t, regs.RSP, data, len); | |||
ret = remote_syscall(t, MYCALL_WRITE, fd, regs.RSP, len); | |||
err = errno; | |||
/* Restore the data */ | |||
memcpy_into_target(t, regs.RSP, backup_data, len); | |||
errno = err; | |||
return ret; | |||
#else | |||
errno = ENOSYS; | |||
return -1; | |||
#endif | |||
} | |||
int mytrace_dup2(struct mytrace *t, int oldfd, int newfd) | |||
{ | |||
#if defined USE_GRAB | |||
return remote_syscall(t, MYCALL_DUP2, oldfd, newfd, 0); | |||
#else | |||
errno = ENOSYS; | |||
return -1; | |||
#endif | |||
} | |||
int mytrace_setpgid(struct mytrace *t, long pid, long pgid) | |||
{ | |||
#if defined USE_GRAB | |||
return remote_syscall(t, MYCALL_SETPGID, pid, pgid, 0); | |||
#else | |||
errno = ENOSYS; | |||
return -1; | |||
#endif | |||
} | |||
int mytrace_setsid(struct mytrace *t) | |||
{ | |||
#if defined USE_GRAB | |||
return remote_syscall(t, MYCALL_SETSID, 0, 0, 0); | |||
#else | |||
errno = ENOSYS; | |||
return -1; | |||
#endif | |||
} | |||
int mytrace_kill(struct mytrace *t, long pid, int sig) | |||
{ | |||
#if defined USE_GRAB | |||
return remote_syscall(t, MYCALL_KILL, pid, sig, 0); | |||
#else | |||
errno = ENOSYS; | |||
return -1; | |||
#endif | |||
} | |||
int mytrace_exit(struct mytrace *t, int status) | |||
{ | |||
#if defined USE_GRAB | |||
ptrace(PTRACE_SETOPTIONS, t->pid, NULL, PTRACE_O_TRACEEXIT); | |||
return remote_syscall(t, MYCALL_EXIT, status, 0, 0); | |||
#else | |||
errno = ENOSYS; | |||
return -1; | |||
#endif | |||
} | |||
int mytrace_exec(struct mytrace *t, char const *command) | |||
{ | |||
#if defined USE_GRAB | |||
struct user_regs_struct regs; | |||
char *env, *p; | |||
long p2, envaddr, argvaddr, envptraddr; | |||
char envpath[PATH_MAX + 1]; | |||
ssize_t envsize = 16 * 1024; | |||
int ret, fd, l, l2; | |||
char *nullp = NULL; | |||
ssize_t r; | |||
ptrace(PTRACE_SETOPTIONS, t->pid, NULL, PTRACE_O_TRACEEXEC); | |||
if (ptrace(PTRACE_GETREGS, t->pid, NULL, ®s) < 0) | |||
{ | |||
perror("PTRACE_GETREGS (exec)\n"); | |||
return -1; | |||
} | |||
debug("PTRACE_GETREGS done"); | |||
env = malloc(envsize); | |||
if (!env) | |||
return -1; | |||
snprintf(envpath, PATH_MAX, "/proc/%d/environ", t->pid); | |||
fd = open(envpath, O_RDONLY); | |||
if (fd == -1) | |||
return -1; | |||
r = read(fd, env, envsize); | |||
close(fd); | |||
if (r == -1) | |||
return -1; | |||
while (r == envsize) | |||
{ | |||
free(env); | |||
env = malloc(envsize); | |||
if (!env) | |||
return -1; | |||
fd = open(envpath, O_RDONLY); | |||
r = read(fd, env, envsize); | |||
close(fd); | |||
if (r == -1) | |||
return -1; | |||
} | |||
envsize = r; | |||
l2 = sizeof(char *); /* Size of a pointer */ | |||
p2 = regs.RSP; | |||
/* First argument is the command string */ | |||
l = strlen(command) + 1; | |||
memcpy_into_target(t, p2, command, l); | |||
p2 += l; | |||
/* Second argument is argv */ | |||
argvaddr = p2; | |||
/* argv[0] is a pointer to the command string */ | |||
memcpy_into_target(t, p2, (char *)®s.RSP, l2); | |||
p2 += l2; | |||
/* Then follows a NULL pointer */ | |||
memcpy_into_target(t, p2, (char *)&nullp, l2); | |||
p2 += l2; | |||
/* Third argument is the environment */ | |||
/* First, copy all the strings */ | |||
memcpy_into_target(t, p2, env, envsize); | |||
envaddr = p2; | |||
p2 += envsize; | |||
/* Then write an array of pointers to the strings */ | |||
envptraddr = p2; | |||
p = env; | |||
while (p < env + envsize) | |||
{ | |||
long diffp = p - env + envaddr; | |||
memcpy_into_target(t, p2, (char *)&diffp, l2); | |||
p2 += l2; | |||
p += strlen(p) + 1; | |||
} | |||
/* And have a NULL pointer at the end of the array */ | |||
memcpy_into_target(t, p2, (char *)&nullp, l2); | |||
free(env); | |||
ret = remote_syscall(t, MYCALL_EXECVE, regs.RSP, argvaddr, envptraddr); | |||
return ret; | |||
#else | |||
errno = ENOSYS; | |||
return -1; | |||
#endif | |||
} | |||
int mytrace_tcgets(struct mytrace *t, int fd, struct termios *tos) | |||
{ | |||
#if defined USE_GRAB | |||
char backup_data[4096]; | |||
struct user_regs_struct regs; | |||
size_t size = sizeof(struct termios); | |||
int ret, err; | |||
if (ptrace(PTRACE_GETREGS, t->pid, NULL, ®s) < 0) | |||
{ | |||
perror("PTRACE_GETREGS (tcgets)\n"); | |||
return -1; | |||
} | |||
/* Backup the data that we will use */ | |||
if (memcpy_from_target(t, backup_data, regs.RSP, size) < 0) | |||
return -1; | |||
ret = remote_syscall(t, MYCALL_IOCTL, fd, TCGETS, regs.RSP); | |||
err = errno; | |||
memcpy_from_target(t, (char *)tos, regs.RSP, size); | |||
/* Restore the data */ | |||
memcpy_into_target(t, regs.RSP, backup_data, size); | |||
errno = err; | |||
return ret; | |||
#else | |||
errno = ENOSYS; | |||
return -1; | |||
#endif | |||
} | |||
int mytrace_tcsets(struct mytrace *t, int fd, struct termios *tos) | |||
{ | |||
#if defined USE_GRAB | |||
char backup_data[4096]; | |||
struct user_regs_struct regs; | |||
size_t size = sizeof(struct termios); | |||
int ret, err; | |||
if (ptrace(PTRACE_GETREGS, t->pid, NULL, ®s) < 0) | |||
{ | |||
perror("PTRACE_GETREGS (tcsets)\n"); | |||
return -1; | |||
} | |||
/* Backup the data that we will use */ | |||
if (memcpy_from_target(t, backup_data, regs.RSP, size) < 0) | |||
return -1; | |||
memcpy_into_target(t, regs.RSP, (char *)tos, size); | |||
ret = remote_syscall(t, MYCALL_IOCTL, fd, TCSETS, regs.RSP); | |||
err = errno; | |||
/* Restore the data */ | |||
memcpy_into_target(t, regs.RSP, backup_data, size); | |||
errno = err; | |||
return ret; | |||
#else | |||
errno = ENOSYS; | |||
return -1; | |||
#endif | |||
} | |||
int mytrace_sctty(struct mytrace *t, int fd) | |||
{ | |||
#if defined USE_GRAB | |||
ptrace(PTRACE_SETOPTIONS, t->pid, NULL, PTRACE_O_TRACEEXIT); | |||
return remote_syscall(t, MYCALL_IOCTL, fd, TIOCSCTTY, 0); | |||
#else | |||
errno = ENOSYS; | |||
return -1; | |||
#endif | |||
} | |||
/* | |||
* XXX: the following functions are local | |||
*/ | |||
#if defined USE_GRAB | |||
static int memcpy_from_target(struct mytrace *t, | |||
char *dest, long src, size_t n) | |||
{ | |||
static int const align = sizeof(long) - 1; | |||
while (n) | |||
{ | |||
long data; | |||
size_t todo = sizeof(long) - (src & align); | |||
if (n < todo) | |||
todo = n; | |||
data = ptrace(PTRACE_PEEKTEXT, t->pid, src - (src & align), 0); | |||
if (errno) | |||
{ | |||
perror("ptrace_peektext (memcpy_from_target)"); | |||
return -1; | |||
} | |||
memcpy(dest, (char *)&data + (src & align), todo); | |||
dest += todo; | |||
src += todo; | |||
n -= todo; | |||
} | |||
return 0; | |||
} | |||
static int memcpy_into_target(struct mytrace *t, | |||
long dest, char const *src, size_t n) | |||
{ | |||
static int const align = sizeof(long) - 1; | |||
while (n) | |||
{ | |||
long data; | |||
size_t todo = sizeof(long) - (dest & align); | |||
if (n < todo) | |||
todo = n; | |||
if (todo != sizeof(long)) | |||
{ | |||
data = ptrace(PTRACE_PEEKTEXT, t->pid, dest - (dest & align), 0); | |||
if (errno) | |||
{ | |||
perror("ptrace_peektext (memcpy_into_target)"); | |||
return -1; | |||
} | |||
} | |||
memcpy((char *)&data + (dest & align), src, todo); | |||
if (ptrace(PTRACE_POKETEXT, t->pid, dest - (dest & align), data) < 0) | |||
{ | |||
perror("ptrace_poketext (memcpy_into_target)"); | |||
return -1; | |||
} | |||
src += todo; | |||
dest += todo; | |||
n -= todo; | |||
} | |||
return 0; | |||
} | |||
static long remote_syscall(struct mytrace *t, long call, | |||
long arg1, long arg2, long arg3) | |||
{ | |||
/* Method for remote syscall: - wait until the traced application exits | |||
from a syscall - save registers - rewind eip/rip to point on the | |||
syscall instruction - single step: execute syscall instruction - | |||
retrieve resulting registers - restore registers */ | |||
struct user_regs_struct regs, oldregs; | |||
long oinst; | |||
int bits; | |||
int offset = 2; | |||
if (call < 0 | |||
|| call >= (long)(sizeof(syscallnames) / sizeof(*syscallnames))) | |||
{ | |||
fprintf(stderr, "unknown remote syscall %li\n", call); | |||
return -1; | |||
} | |||
debug("remote syscall %s(0x%lx, 0x%lx, 0x%lx)", | |||
syscallnames[call], arg1, arg2, arg3); | |||
#if defined __x86_64__ | |||
bits = 64; | |||
#else | |||
bits = 32; | |||
#endif | |||
for (;;) | |||
{ | |||
if (ptrace(PTRACE_GETREGS, t->pid, NULL, &oldregs) < 0) | |||
{ | |||
perror("PTRACE_GETREGS (syscall 1)\n"); | |||
return -1; | |||
} | |||
oinst = ptrace(PTRACE_PEEKTEXT, t->pid, oldregs.RIP - 2, 0) & 0xffff; | |||
#if defined __x86_64__ | |||
if (oinst == SYSCALL_AMD64) | |||
break; | |||
#endif | |||
if (oinst == SYSCALL_X86 || oinst == SYSCALL_X86_NEW) | |||
{ | |||
bits = 32; | |||
break; | |||
} | |||
if (ptrace(PTRACE_SYSCALL, t->pid, NULL, 0) < 0) | |||
{ | |||
perror("ptrace_syscall (1)"); | |||
return -1; | |||
} | |||
waitpid(t->pid, NULL, 0); | |||
if (ptrace(PTRACE_SYSCALL, t->pid, NULL, 0) < 0) | |||
{ | |||
perror("ptrace_syscall (2)"); | |||
return -1; | |||
} | |||
waitpid(t->pid, NULL, 0); | |||
} | |||
print_registers(t->pid); | |||
if (oinst == SYSCALL_X86_NEW) | |||
{ | |||
/* Get back to sysenter */ | |||
while ((ptrace(PTRACE_PEEKTEXT, t->pid, oldregs.RIP - offset, 0) & | |||
0xffff) != 0x340f) | |||
offset++; | |||
oldregs.RBP = oldregs.RSP; | |||
} | |||
regs = oldregs; | |||
regs.RIP = regs.RIP - offset; | |||
#if defined __x86_64__ | |||
if (bits == 64) | |||
{ | |||
regs.RAX = syscalls64[call]; | |||
regs.RDI = arg1; | |||
regs.RSI = arg2; | |||
regs.RDX = arg3; | |||
} | |||
else | |||
#endif | |||
{ | |||
regs.RAX = syscalls32[call]; | |||
regs.RBX = arg1; | |||
regs.RCX = arg2; | |||
regs.RDX = arg3; | |||
} | |||
if (ptrace(PTRACE_SETREGS, t->pid, NULL, ®s) < 0) | |||
{ | |||
perror("PTRACE_SETREGS (syscall 1)\n"); | |||
return -1; | |||
} | |||
for (;;) | |||
{ | |||
int status; | |||
print_registers(t->pid); | |||
if (ptrace(PTRACE_SINGLESTEP, t->pid, NULL, NULL) < 0) | |||
{ | |||
perror("PTRACE_SINGLESTEP (syscall)\n"); | |||
return -1; | |||
} | |||
waitpid(t->pid, &status, 0); | |||
if (WIFEXITED(status)) | |||
return 0; | |||
if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP) | |||
continue; | |||
/* Fuck Linux: there is no macro for this */ | |||
switch ((status >> 16) & 0xffff) | |||
{ | |||
case PTRACE_EVENT_FORK: | |||
if (ptrace(PTRACE_GETEVENTMSG, t->pid, 0, &t->child) < 0) | |||
{ | |||
perror("PTRACE_GETEVENTMSG (syscall)\n"); | |||
return -1; | |||
} | |||
debug("PTRACE_GETEVENTMSG %d", t->child); | |||
continue; | |||
case PTRACE_EVENT_EXIT: | |||
debug("PTRACE_EVENT_EXIT"); | |||
/* The process is about to exit, don't do anything else */ | |||
return 0; | |||
case PTRACE_EVENT_EXEC: | |||
debug("PTRACE_EVENT_EXEC"); | |||
return 0; | |||
} | |||
break; | |||
} | |||
print_registers(t->pid); | |||
if (ptrace(PTRACE_GETREGS, t->pid, NULL, ®s) < 0) | |||
{ | |||
perror("PTRACE_GETREGS (syscall 2)\n"); | |||
return -1; | |||
} | |||
if (ptrace(PTRACE_SETREGS, t->pid, NULL, &oldregs) < 0) | |||
{ | |||
perror("PTRACE_SETREGS (syscall 2)\n"); | |||
return -1; | |||
} | |||
print_registers(t->pid); | |||
debug("syscall %s returned %ld", syscallnames[call], regs.RAX); | |||
if ((long)regs.RAX < 0) | |||
{ | |||
errno = -(long)regs.RAX; | |||
perror("syscall"); | |||
return -1; | |||
} | |||
return regs.RAX; | |||
} | |||
/* For debugging purposes only. Prints register and stack information. */ | |||
#if defined DEBUG | |||
static void print_registers(pid_t pid) | |||
{ | |||
union | |||
{ | |||
long int l; | |||
unsigned char data[sizeof(long int)]; | |||
} inst; | |||
struct user_regs_struct regs; | |||
int i; | |||
if (ptrace(PTRACE_GETREGS, pid, NULL, ®s) < 0) | |||
{ | |||
perror("PTRACE_GETREGS (syscall 2)"); | |||
exit(errno); | |||
} | |||
fprintf(stderr, " / %s: " FMT " ", STRINGIFY(RAX), regs.RAX); | |||
fprintf(stderr, "%s: " FMT "\n", STRINGIFY(RBX), regs.RBX); | |||
fprintf(stderr, " | %s: " FMT " ", STRINGIFY(RCX), regs.RCX); | |||
fprintf(stderr, "%s: " FMT "\n", STRINGIFY(RDX), regs.RDX); | |||
fprintf(stderr, " | %s: " FMT " ", STRINGIFY(RDI), regs.RDI); | |||
fprintf(stderr, "%s: " FMT "\n", STRINGIFY(RSI), regs.RSI); | |||
fprintf(stderr, " | %s: " FMT " ", STRINGIFY(RSP), regs.RSP); | |||
fprintf(stderr, "%s: " FMT "\n", STRINGIFY(RIP), regs.RIP); | |||
inst.l = ptrace(PTRACE_PEEKTEXT, pid, regs.RIP - 4, 0); | |||
fprintf(stderr, " | code: ... %02x %02x %02x %02x <---> ", | |||
inst.data[0], inst.data[1], inst.data[2], inst.data[3]); | |||
inst.l = ptrace(PTRACE_PEEKTEXT, pid, regs.RIP, 0); | |||
fprintf(stderr, "%02x %02x %02x %02x ...\n", | |||
inst.data[0], inst.data[1], inst.data[2], inst.data[3]); | |||
fprintf(stderr, " \\ stack: ... "); | |||
for (i = -16; i < 24; i += sizeof(long)) | |||
{ | |||
inst.l = ptrace(PTRACE_PEEKDATA, pid, regs.RSP + i, 0); | |||
#if defined __x86_64__ | |||
fprintf(stderr, "%02x %02x %02x %02x %02x %02x %02x %02x ", | |||
inst.data[0], inst.data[1], inst.data[2], inst.data[3], | |||
inst.data[4], inst.data[5], inst.data[6], inst.data[7]); | |||
#else | |||
fprintf(stderr, "%02x %02x %02x %02x ", | |||
inst.data[0], inst.data[1], inst.data[2], inst.data[3]); | |||
#endif | |||
if (i == 0) | |||
fprintf(stderr, "[%s] ", STRINGIFY(RSP)); | |||
} | |||
fprintf(stderr, "...\n"); | |||
} | |||
#endif /* DEBUG */ | |||
#endif /* USE_GRAB */ |
@@ -0,0 +1,33 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2008-2010 Sam Hocevar <sam@hocevar.net> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#include <termios.h> | |||
struct mytrace; | |||
struct mytrace* mytrace_attach(long int pid); | |||
struct mytrace* mytrace_fork(struct mytrace *t); | |||
int mytrace_detach(struct mytrace *t); | |||
long mytrace_getpid(struct mytrace *t); | |||
int mytrace_open(struct mytrace *t, char const *path, int mode); | |||
int mytrace_write(struct mytrace *t, int fd, char const *data, size_t len); | |||
int mytrace_close(struct mytrace *t, int fd); | |||
int mytrace_dup2(struct mytrace *t, int oldfd, int newfd); | |||
int mytrace_setpgid(struct mytrace *t, long pid, long pgid); | |||
int mytrace_setsid(struct mytrace *t); | |||
int mytrace_kill(struct mytrace *t, long pid, int sig); | |||
int mytrace_exit(struct mytrace *t, int status); | |||
int mytrace_exec(struct mytrace *t, char const *command); | |||
int mytrace_tcgets(struct mytrace *t, int fd, struct termios *tos); | |||
int mytrace_tcsets(struct mytrace *t, int fd, struct termios *tos); | |||
int mytrace_sctty(struct mytrace *t, int fd); |
@@ -0,0 +1,430 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net> | |||
* 2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#ifndef _NEERCS_H_ | |||
#define _NEERCS_H_ | |||
#include <stdint.h> | |||
#include <caca.h> | |||
#include "widgets.h" | |||
enum wm_types | |||
{ | |||
WM_FULL, | |||
WM_CARD, | |||
WM_HSPLIT, | |||
WM_VSPLIT, | |||
WM_MAX, | |||
}; | |||
enum mouse_report | |||
{ | |||
MOUSE_NONE, | |||
MOUSE_X10, | |||
MOUSE_VT200, | |||
MOUSE_VT200_HIGHLIGHT, | |||
MOUSE_BTN_EVENT, | |||
MOUSE_ANY_EVENT, | |||
}; | |||
/* ISO-2022 Conversion State */ | |||
struct iso2022_conv_state | |||
{ | |||
/* cs = coding system/coding method: */ | |||
/* (with standard return) */ | |||
/* '@' = ISO-2022, */ | |||
/* 'G' = UTF-8 without implementation level, */ | |||
/* '8' = UTF-8 (Linux console and imitators), */ | |||
/* and many others that are rarely used; */ | |||
/* (without standard return) */ | |||
/* '/G' = UTF-8 Level 1, */ | |||
/* '/H' = UTF-8 Level 2, */ | |||
/* '/I' = UTF-8 Level 3, */ | |||
/* and many others that are rarely used */ | |||
uint32_t cs; | |||
/* ctrl8bit = allow 8-bit controls */ | |||
uint8_t ctrl8bit; | |||
/* cn[0] = C0 control charset (0x00 ... 0x1f): | |||
* '@' = ISO 646, | |||
* '~' = empty, | |||
* and many others that are rarely used */ | |||
/* cn[1] = C1 control charset (0x80 ... 0x9f): | |||
* 'C' = ISO 6429-1983, | |||
* '~' = empty, | |||
* and many others that are rarely used */ | |||
uint32_t cn[2]; | |||
/* glr[0] = GL graphic charset (94-char. 0x21 ... 0x7e, | |||
* 94x94-char. 0x21/0x21 ... 0x7e/0x7e), | |||
* and | |||
* glr[1] = GR graphic charset (94-char. 0xa1 ... 0xfe, | |||
* 96-char. 0xa0 ... 0xff, | |||
* 94x94-char. 0xa1/0xa1 ... 0xfe/0xfe, | |||
* 96x96-char. 0xa0/0xa0 ... 0xff/0xff): | |||
* 0 = G0, 1 = G1, 2 = G2, 3 = G3 */ | |||
uint8_t glr[2]; | |||
/* gn[i] = G0/G1/G2/G3 graphic charset state: | |||
* (94-char. sets) | |||
* '0' = DEC ACS (VT100 and imitators), | |||
* 'B' = US-ASCII, | |||
* and many others that are rarely used for e.g. various national ASCII variations; | |||
* (96-char. sets) | |||
* '.A' = ISO 8859-1 "Latin 1" GR, | |||
* '.~' = empty 96-char. set, | |||
* and many others that are rarely used for e.g. ISO 8859-n GR; | |||
* (double-byte 94x94-charsets) | |||
* '$@' = Japanese Character Set ("old JIS") (JIS C 6226:1978), | |||
* '$A' = Chinese Character Set (GB 2312), | |||
* '$B' = Japanese Character Set (JIS X0208/JIS C 6226:1983), | |||
* '$C' = Korean Graphic Character Set (KSC 5601:1987), | |||
* '$D' = Supplementary Japanese Graphic Character Set (JIS X0212), | |||
* '$E' = CCITT Chinese Set (GB 2312 + GB 8565), | |||
* '$G' = CNS 11643 plane 1, | |||
* '$H' = CNS 11643 plane 2, | |||
* '$I' = CNS 11643 plane 3, | |||
* '$J' = CNS 11643 plane 4, | |||
* '$K' = CNS 11643 plane 5, | |||
* '$L' = CNS 11643 plane 6, | |||
* '$M' = CNS 11643 plane 7, | |||
* '$O' = JIS X 0213 plane 1, | |||
* '$P' = JIS X 0213 plane 2, | |||
* '$Q' = JIS X 0213-2004 Plane 1, | |||
* and many others that are rarely used for e.g. traditional | |||
* ideographic Vietnamese and BlissSymbolics; | |||
* (double-byte 96x96-charsets) | |||
* none standardized or in use on terminals AFAIK (Mule does use | |||
* some internally) | |||
*/ | |||
uint32_t gn[4]; | |||
/* ss = single-shift state: 0 = GL, 2 = G2, 3 = G3 */ | |||
uint8_t ss; | |||
}; | |||
struct screen | |||
{ | |||
/* Graphics stuff */ | |||
int init; | |||
caca_canvas_t *cv; | |||
uint32_t clearattr; | |||
uint8_t fg, bg; /* ANSI-context fg/bg */ | |||
uint8_t dfg, dbg; /* Default fg/bg */ | |||
uint8_t bold, blink, italics, negative, concealed, underline; | |||
uint8_t faint, strike, proportional; /* unsupported */ | |||
struct iso2022_conv_state conv_state; /* charset mess */ | |||
/* Other stuff */ | |||
int visible; /* Draw canvas and border flag */ | |||
int fd; /* pty fd */ | |||
unsigned char *buf; /* text buffer */ | |||
long int total; /* buffer length */ | |||
char *title; /* tty title */ | |||
int bell; /* bell occuring */ | |||
unsigned int scroll, s1, s2; /* FIXME, ANSI scroll properties */ | |||
int pid; /* running program pid */ | |||
int changed; /* content was updated */ | |||
int x, y; /* Canvas position */ | |||
int w, h; /* Canvas size */ | |||
int orig_x, orig_y; /* Used by recurrents */ | |||
int orig_w, orig_h; /* Used by recurrents */ | |||
int report_mouse; /* ANSI */ | |||
}; | |||
enum socket_type | |||
{ | |||
SOCK_SERVER=0, | |||
SOCK_CLIENT=1 | |||
}; | |||
struct cube_props | |||
{ | |||
int in_switch; | |||
int side; | |||
long long unsigned int duration; | |||
}; | |||
struct interpreter_props | |||
{ | |||
/* Input box */ | |||
struct input_box *box; | |||
}; | |||
struct screensaver | |||
{ | |||
/* ScreenSaver stuff */ | |||
long long unsigned int timeout; /* Screensaver timeout in us */ | |||
int in_screensaver; | |||
void *data; | |||
}; | |||
struct comm | |||
{ | |||
/* Detaching */ | |||
int attached; /* Are we attached to a terminal or a server */ | |||
int socket[2]; /* Sockets to write to the server / to the client */ | |||
char *socket_path[2]; /* Sockets to write to the server / to the client */ | |||
char *socket_dir; /* Where to create the socket */ | |||
char *session_name; /* Name of the session */ | |||
}; | |||
struct lock | |||
{ | |||
int locked; | |||
int lock_offset; | |||
int lock_on_detach; | |||
long long unsigned int autolock_timeout; | |||
char lockpass[1024]; | |||
char lockmsg[1024]; | |||
}; | |||
struct modals | |||
{ | |||
/* Add-ons*/ | |||
int mini; /* Thumbnails */ | |||
int status; /* Status bar */ | |||
int help; /* Help */ | |||
int python_command; /* Python command */ | |||
int window_list; /* Window list */ | |||
int cur_in_list; /* Window list */ | |||
}; | |||
struct sys | |||
{ | |||
char *default_shell; | |||
char *user_path; | |||
int *to_grab; | |||
char **to_start; | |||
int nb_to_grab; | |||
int attach, forceattach; | |||
}; | |||
struct screen_list | |||
{ | |||
int outfd; /* Debug */ | |||
int in_bell; /* Bell occuring in a window */ | |||
int was_in_bell; | |||
int dont_update_coords; /* Used by recurrents */ | |||
int changed; /* Global redraw (e.g. adding a screen) */ | |||
int delay; /* Minimal time between two refresh (ms) */ | |||
int requested_delay; | |||
int force_refresh; | |||
int need_refresh; /* If we skipped a refresh, do it next time */ | |||
int command; | |||
int eyecandy; /* Eye Candy */ | |||
long long unsigned int last_key_time; | |||
long long unsigned int last_refresh_time; | |||
struct comm comm; /* Client/Server communications */ | |||
struct lock lock; /* Lock */ | |||
struct modals modals; /* Modal windows */ | |||
struct interpreter_props interpreter_props; /* Python interpreter */ | |||
struct screensaver screensaver;/* Screensaver stuff */ | |||
int pty, prevpty; /* Current and previous window */ | |||
int count; /* Window count */ | |||
int wm_type; /* Window manager type */ | |||
int border_size; /* Borders */ | |||
struct cube_props cube; /* Cube */ | |||
long long unsigned int last_switch; /* Cube */ | |||
struct screen **screen; /* Windows */ | |||
struct option *config; /* Option parsing and configuration */ | |||
struct sys sys; /* System stuff */ | |||
struct recurrent_list *recurrent_list; /* Recurrents functions */ | |||
char *title; /* Window title */ | |||
int width, height; /* caca window size */ | |||
int old_x, old_y; /* Mouse */ | |||
int mouse_button; | |||
caca_canvas_t *cv; | |||
caca_display_t *dp; | |||
}; | |||
/* Configuration */ | |||
struct option | |||
{ | |||
char *key; | |||
char *value; | |||
struct option *next; | |||
}; | |||
struct config_line | |||
{ | |||
const char name[32]; | |||
int (*set) (const char *argv, struct screen_list * screen_list); | |||
char* (*get) (struct screen_list * screen_list); | |||
}; | |||
/* Recurrents */ | |||
struct recurrent | |||
{ | |||
int (*function)(struct screen_list*, struct recurrent* rec, void *user, long long unsigned int t); | |||
void *user; | |||
long long unsigned int start_time; | |||
int kill_me; | |||
}; | |||
struct recurrent_list | |||
{ | |||
int count; | |||
struct recurrent **recurrent; | |||
}; | |||
void version(void); | |||
void usage(int argc, char **argv); | |||
struct screen_list *init_neercs(int argc, char **argv); | |||
int handle_command_line(int argc, char *argv[], struct screen_list *screen_list); | |||
struct screen_list *create_screen_list(void); | |||
void free_screen_list(struct screen_list *screen_list); | |||
int start_client(struct screen_list * screen_list); | |||
/** \defgroup client neercs client | |||
* @{ */ | |||
void mainloop(struct screen_list *screen_list); | |||
int mainloop_tick(char **pbuf, struct screen_list *screen_list); | |||
/** }@ */ | |||
int create_pty(char *cmd, unsigned int w, unsigned int h, int *cpid); | |||
int create_pty_grab(long pid, unsigned int w, unsigned int h, int *cpid); | |||
int grab_process(long pid, char *ptyname, int ptyfd, int *newpid); | |||
long select_process(struct screen_list* screen_list); | |||
long int import_term(struct screen_list *screen_list, struct screen *sc, void const *data, unsigned int size); | |||
int set_tty_size(int fd, unsigned int w, unsigned int h); | |||
int update_terms(struct screen_list* screen_list); | |||
void refresh_screens(struct screen_list *screen_list); | |||
int update_screens_contents(struct screen_list* screen_list); | |||
int install_fds(struct screen_list *screen_list); | |||
long long get_us(void); | |||
void attach(struct screen_list* screen_list); | |||
int detach(struct screen_list* screen_list); | |||
int request_attach(struct screen_list* screen_list); | |||
char * build_socket_path(char *socket_dir, char *session_name, enum socket_type socktype); | |||
int create_socket(struct screen_list* screen_list, enum socket_type socktype); | |||
char * connect_socket(struct screen_list* screen_list, enum socket_type socktype); | |||
char ** list_sockets(char *socket_dir, char *session_name); | |||
int start_server(struct screen_list *screen_list); | |||
int send_event(caca_event_t ev, struct screen_list* screen_list); | |||
int send_delay(struct screen_list* screen_list); | |||
int send_ansi_sequence(struct screen_list *screen_list, char *str); | |||
/* Screens management */ | |||
struct screen* create_screen(int w, int h, char *command); | |||
struct screen* create_screen_grab(int w, int h, int pid); | |||
int destroy_screen(struct screen *s); | |||
int add_screen(struct screen_list *list, struct screen *s); | |||
int remove_screen(struct screen_list *list, int n, int please_kill); | |||
void resize_screen(struct screen *s, int z, int h); | |||
/* Window managers */ | |||
void update_windows_props(struct screen_list *screen_list); | |||
void update_windows_props_cards(struct screen_list *screen_list); | |||
void update_windows_props_hsplit(struct screen_list *screen_list); | |||
void update_windows_props_full(struct screen_list *screen_list); | |||
void update_windows_props_vsplit(struct screen_list *screen_list); | |||
void update_windows_props_cube(struct screen_list *screen_list); | |||
void wm_refresh(struct screen_list *screen_list); | |||
void wm_refresh_card(struct screen_list *screen_list); | |||
void wm_refresh_cube(struct screen_list *screen_list); | |||
void wm_refresh_full(struct screen_list *screen_list); | |||
void wm_refresh_hsplit(struct screen_list *screen_list); | |||
void wm_refresh_vsplit(struct screen_list *screen_list); | |||
int switch_screen_recurrent(struct screen_list* screen_list, struct recurrent* rec, void *user, long long unsigned int t); | |||
/* Effects and addons */ | |||
void draw_thumbnails(struct screen_list *screen_list); | |||
void draw_status(struct screen_list *screen_list); | |||
void draw_help(struct screen_list *screen_list); | |||
int help_handle_key(struct screen_list *screen_list, unsigned int c); | |||
int update_window_list(int c, struct screen_list *screen_list); | |||
void draw_list(struct screen_list *screen_list); | |||
void draw_lock(struct screen_list *screen_list); | |||
int update_lock(int c, struct screen_list *screen_list); | |||
int validate_lock(struct screen_list *screen_list, char *user, char *pass); | |||
int close_screen_recurrent(struct screen_list*, struct recurrent* rec, void *user, long long unsigned int t); | |||
/* Input to ANSI */ | |||
void *convert_input_ansi(unsigned int *c, int *size); | |||
int handle_command_input(struct screen_list*screen_list, unsigned int c); | |||
/* Screensavers */ | |||
void screensaver_init(struct screen_list *screen_list); | |||
void screensaver_kill(struct screen_list *screen_list); | |||
void draw_screensaver(struct screen_list *screen_list); | |||
void screensaver_flying_toasters(struct screen_list *screen_list); | |||
void screensaver_flying_toasters_init(struct screen_list *screen_list); | |||
void screensaver_flying_toasters_kill(struct screen_list *screen_list); | |||
/* Actions */ | |||
void dump_to_file(struct screen_list *screen_list); | |||
/* Recurrents */ | |||
int handle_recurrents(struct screen_list* screen_list); | |||
int add_recurrent(struct recurrent_list *list, | |||
int (*function)(struct screen_list*, struct recurrent* rec, void *user, long long unsigned int t), | |||
void *user); | |||
int remove_recurrent(struct recurrent_list *list, int n); | |||
/* Configuration file */ | |||
int read_configuration_file(char *filename, struct screen_list *screen_list); | |||
int parse_conf_line(char *buf, int size, struct screen_list *screen_list); | |||
int get_key_value(char *line, struct option *option); | |||
int fill_config(struct screen_list *screen_list); | |||
struct config_line *get_config(const char *name); | |||
struct config_line *get_config_option(void); | |||
/* Python interpreter */ | |||
#ifdef USE_PYTHON | |||
int python_init(struct screen_list *sl); | |||
int python_close(struct screen_list *sl); | |||
int python_command_handle_key(struct screen_list *screen_list, unsigned int c); | |||
void draw_python_command(struct screen_list *screen_list); | |||
#endif | |||
#if defined DEBUG | |||
# include <stdio.h> | |||
# include <stdarg.h> | |||
static inline void debug(const char *format, ...) | |||
{ | |||
va_list args; | |||
va_start(args, format); | |||
fprintf(stderr, "** neercs debug ** "); | |||
vfprintf(stderr, format, args); | |||
fprintf(stderr, "\n"); | |||
va_end(args); | |||
} | |||
#else | |||
# define debug(format, ...) do {} while(0) | |||
#endif | |||
#endif /* _NEERCS_H_ */ |
@@ -0,0 +1,197 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2009-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#include "config.h" | |||
#ifdef USE_PYTHON | |||
#include <Python.h> | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <unistd.h> | |||
#include <fcntl.h> | |||
#include <signal.h> | |||
#include <sys/ioctl.h> | |||
#include <sys/socket.h> | |||
#include <sys/time.h> | |||
#include <time.h> | |||
#include <sys/wait.h> | |||
#include <sys/types.h> | |||
#include <caca.h> | |||
#include "neercs.h" | |||
#include "py_module.h" | |||
static int python_execute(struct screen_list *sl); | |||
char *getStringFromPyObject(PyObject * p); | |||
static char *getPythonError(void); | |||
#if ! defined HAVE_PYTHON26 | |||
static PyObject *PyUnicode_FromString(char const *str) | |||
{ | |||
PyObject *tmp, *ret; | |||
tmp = PyString_FromString(str); | |||
if (!tmp) | |||
return NULL; | |||
ret = PyString_AsDecodedObject(tmp, "utf-8", "replace"); | |||
Py_DECREF(tmp); | |||
return ret; | |||
} | |||
#endif | |||
int python_command_handle_key(struct screen_list *screen_list, unsigned int c) | |||
{ | |||
int ret = widget_ibox_handle_key(screen_list->interpreter_props.box, c); | |||
if (ret == INPUT_BOX_ESC) | |||
{ | |||
widget_ibox_destroy(screen_list->interpreter_props.box); | |||
screen_list->interpreter_props.box = NULL; | |||
screen_list->modals.python_command = 0; | |||
} | |||
else if (ret == INPUT_BOX_RET) | |||
{ | |||
python_execute(screen_list); | |||
} | |||
screen_list->changed = 1; | |||
return 1; | |||
} | |||
void draw_python_command(struct screen_list *screen_list) | |||
{ | |||
if (!screen_list->interpreter_props.box) | |||
{ | |||
screen_list->interpreter_props.box = | |||
widget_ibox_init(screen_list->cv, 65, 6); | |||
debug("py Init %p\n", screen_list->interpreter_props.box); | |||
} | |||
widget_ibox_draw(screen_list->interpreter_props.box); | |||
} | |||
/* Actual Python interpreter stuff */ | |||
int python_init(struct screen_list *sl) | |||
{ | |||
initNeercsModule(sl); | |||
return 0; | |||
} | |||
int python_close(struct screen_list *sl) | |||
{ | |||
widget_ibox_destroy(sl->interpreter_props.box); | |||
sl->interpreter_props.box = NULL; | |||
Py_Finalize(); | |||
return 0; | |||
} | |||
static int python_execute(struct screen_list *sl) | |||
{ | |||
char *command = widget_ibox_get_text(sl->interpreter_props.box); | |||
if (!command || strlen(command) < 1) | |||
return -1; | |||
int err = 0; | |||
debug("py Executing '%s'\n", command); | |||
PyObject *pModule, *pName; | |||
/* Module from which to call the function */ | |||
pName = PyUnicode_FromString("neercs"); | |||
if (!pName) | |||
{ | |||
widget_ibox_set_error(sl->interpreter_props.box, getPythonError()); | |||
err = 1; | |||
debug("py Error 1\n"); | |||
goto end; | |||
} | |||
pModule = PyImport_Import(pName); | |||
Py_DECREF(pName); | |||
if (pModule != NULL) | |||
{ | |||
PyObject *dictionary = PyModule_GetDict(pModule); | |||
getExportedValues(dictionary); | |||
PyObject *o = | |||
PyRun_String(command, Py_single_input, | |||
dictionary, NULL); | |||
if (!o) | |||
{ | |||
widget_ibox_set_error(sl->interpreter_props.box, getPythonError()); | |||
err = 1; | |||
} | |||
else | |||
{ | |||
setExportedValues(dictionary); | |||
widget_ibox_set_msg(sl->interpreter_props.box, getStringFromPyObject(o)); | |||
err = 1; | |||
} | |||
Py_DECREF(pModule); | |||
} | |||
else | |||
{ | |||
widget_ibox_set_error(sl->interpreter_props.box, getPythonError()); | |||
err = 1; | |||
} | |||
end: | |||
if (!err) | |||
{ | |||
widget_ibox_destroy(sl->interpreter_props.box); | |||
sl->interpreter_props.box = NULL; | |||
sl->modals.python_command = 0; | |||
} | |||
sl->changed = 1; | |||
return 0; | |||
} | |||
static char *getPythonError(void) | |||
{ | |||
char *err; | |||
PyObject *type, *value, *traceback; | |||
PyErr_Fetch(&type, &value, &traceback); | |||
char *evalue = getStringFromPyObject(value); | |||
int r = asprintf(&err, "%s", evalue); | |||
(void)r; | |||
return err; | |||
} | |||
char *getStringFromPyObject(PyObject * p) | |||
{ | |||
PyObject *str = PyObject_Repr(p); | |||
char *tmp; | |||
#if defined HAVE_PYTHON26 | |||
tmp = PyBytes_AS_STRING(PyUnicode_AsEncodedString(str, "utf-8", "Error ~")); | |||
#else | |||
tmp = PyString_AsString(str); | |||
#endif | |||
return strdup(tmp); | |||
} | |||
#endif |
@@ -0,0 +1,193 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2009-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#include "config.h" | |||
#ifdef USE_PYTHON | |||
#include <Python.h> | |||
#include "py_module.h" | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <unistd.h> | |||
#include <fcntl.h> | |||
#include <signal.h> | |||
#include <sys/ioctl.h> | |||
#include <sys/socket.h> | |||
#include <sys/time.h> | |||
#include <time.h> | |||
#include <sys/wait.h> | |||
#include <sys/types.h> | |||
#include <caca.h> | |||
#include "neercs.h" | |||
/* FIXME : Find a way to pass a user pointer to PyModuleDef or something */ | |||
static struct screen_list *screen_list; | |||
#if defined HAVE_PYTHON3 | |||
static PyObject *PyInit_neercs(void); | |||
#else | |||
static void PyInit_neercs(void); | |||
#endif | |||
static void removeTrailingStuff(char *b); | |||
static void addVariableFromConfig(PyObject * dictionary, | |||
const char *varname, const char *configname) | |||
{ | |||
char *v = get_config(configname)->get(screen_list); | |||
if (v != NULL) | |||
{ | |||
PyObject *value = Py_BuildValue("s", v); | |||
PyDict_SetItemString(dictionary, varname, value); | |||
} | |||
debug("py get '%s' to '%s'\n", varname, | |||
get_config(configname)->get(screen_list)); | |||
} | |||
static void removeTrailingStuff(char *b) | |||
{ | |||
if(!b) | |||
return; | |||
if(b[0]=='\'') | |||
{ | |||
memmove(b, &b[1], strlen(b)-1); | |||
b[strlen(b)-2] = 0; | |||
} | |||
} | |||
void setExportedValues(PyObject * dictionary) | |||
{ | |||
struct config_line *config_option = get_config_option(); | |||
int i = 0; | |||
while (strncmp(config_option[i].name, "last", strlen("last"))) | |||
{ | |||
/* Get variable */ | |||
PyObject *res = | |||
PyDict_GetItemString(dictionary, config_option[i].name); | |||
/* Got it */ | |||
if (res) | |||
{ | |||
/* Get object representation | |||
* FIXME : find a way to check object's type */ | |||
PyObject *str = PyObject_Repr(res); | |||
/* Make sure it's a string */ | |||
char *err = | |||
#if defined HAVE_PYTHON3 | |||
PyBytes_AS_STRING(PyUnicode_AsEncodedString | |||
(str, "utf-8", "Error ~")); | |||
#elif defined HAVE_PYTHON2 | |||
PyString_AsString(str); | |||
#endif | |||
/* FIXME leak leak leak */ | |||
char *s = strdup(err); | |||
if (s != NULL) | |||
{ | |||
/* Representation can include '' around strings */ | |||
removeTrailingStuff(s); | |||
get_config(config_option[i].name)->set(s, screen_list); | |||
} | |||
} | |||
i++; | |||
} | |||
} | |||
void getExportedValues(PyObject * dictionary) | |||
{ | |||
struct config_line *config_option = get_config_option(); | |||
int i = 0; | |||
while (strncmp(config_option[i].name, "last", strlen("last"))) | |||
{ | |||
addVariableFromConfig(dictionary, config_option[i].name, | |||
config_option[i].name); | |||
i++; | |||
} | |||
} | |||
static PyObject *neercs_get(PyObject * self, PyObject * args) | |||
{ | |||
char *s = NULL; | |||
debug("Get using list at %p", screen_list); | |||
if (!PyArg_ParseTuple(args, "s", &s)) | |||
{ | |||
PyErr_SetString(PyExc_ValueError, "Can't parse argument"); | |||
debug("py Can't parse"); | |||
return NULL; | |||
} | |||
debug("py Argument : '%s'", s); | |||
struct config_line *c = get_config(s); | |||
if (c) | |||
return Py_BuildValue("s", c->get(screen_list)); | |||
PyErr_SetString(PyExc_ValueError, | |||
"Can't get value for specified variable"); | |||
return NULL; | |||
} | |||
static PyObject *neercs_version(PyObject * self, PyObject * args) | |||
{ | |||
return Py_BuildValue("s", PACKAGE_VERSION); | |||
} | |||
static PyMethodDef NeercsMethods[] = | |||
{ | |||
{ "version", neercs_version, METH_NOARGS, "Return the neercs version." }, | |||
{ "get", neercs_get, METH_VARARGS, | |||
"Return the specified variable's value." }, | |||
{ NULL, NULL, 0, NULL } | |||
}; | |||
#if defined HAVE_PYTHON3 | |||
static PyObject *PyInit_neercs(void) | |||
{ | |||
static PyModuleDef NeercsModule = | |||
{ | |||
PyModuleDef_HEAD_INIT, "neercs", NULL, -1, NeercsMethods, | |||
NULL, NULL, NULL, NULL | |||
}; | |||
return PyModule_Create(&NeercsModule); | |||
} | |||
#elif defined HAVE_PYTHON2 | |||
static void PyInit_neercs(void) | |||
{ | |||
PyMethodDef *m = NeercsMethods; | |||
PyObject *mod = PyModule_New("neercs"); | |||
PyModule_AddStringConstant(mod, "__file__", "<synthetic>"); | |||
for (m = NeercsMethods; m->ml_name; m++) | |||
PyModule_AddObject(mod, m->ml_name, PyCFunction_New(m, NULL)); | |||
} | |||
#endif | |||
void initNeercsModule(struct screen_list *sl) | |||
{ | |||
screen_list = sl; | |||
PyImport_AppendInittab("neercs", &PyInit_neercs); | |||
Py_Initialize(); | |||
} | |||
#endif |
@@ -0,0 +1,25 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2009-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#include "config.h" | |||
#ifdef USE_PYTHON | |||
#include <Python.h> | |||
#include "neercs.h" | |||
void initNeercsModule(struct screen_list *sl); | |||
void getExportedValues(PyObject * dictionary); | |||
void setExportedValues(PyObject * dictionary); | |||
#endif |
@@ -0,0 +1,112 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net> | |||
* 2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#include "config.h" | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <stdlib.h> | |||
#include <sys/types.h> | |||
#include <signal.h> | |||
#include <sys/wait.h> | |||
#include <errno.h> | |||
#include <unistd.h> | |||
#include "neercs.h" | |||
int handle_recurrents(struct screen_list *screen_list) | |||
{ | |||
int refresh = 0, i; | |||
/* Recurrent functions */ | |||
for (i = 0; i < screen_list->recurrent_list->count; i++) | |||
{ | |||
if (screen_list->recurrent_list->recurrent[i]->function) | |||
{ | |||
refresh |= | |||
screen_list->recurrent_list->recurrent[i]-> | |||
function(screen_list, | |||
screen_list->recurrent_list->recurrent[i], | |||
screen_list->recurrent_list->recurrent[i]->user, | |||
get_us()); | |||
} | |||
} | |||
/* Delete recurrent functions */ | |||
for (i = 0; i < screen_list->recurrent_list->count; i++) | |||
{ | |||
if (screen_list->recurrent_list->recurrent[i]->kill_me) | |||
{ | |||
remove_recurrent(screen_list->recurrent_list, i); | |||
refresh = 1; | |||
i = 0; | |||
} | |||
} | |||
return refresh; | |||
} | |||
/* Add recurrent function. It will be called at each main loop iteration, | |||
unless it is removed */ | |||
int add_recurrent(struct recurrent_list *list, | |||
int (*func) (struct screen_list *, struct recurrent * rec, | |||
void *user, long long unsigned int t), | |||
void *user) | |||
{ | |||
if (list == NULL || func == NULL) | |||
return -1; | |||
list->recurrent = (struct recurrent **)realloc(list->recurrent, | |||
sizeof(struct recurrent *) | |||
* (list->count + 1)); | |||
if (!list->recurrent) | |||
fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, | |||
__LINE__); | |||
list->recurrent[list->count] = malloc(sizeof(struct recurrent)); | |||
list->recurrent[list->count]->kill_me = 0; | |||
list->recurrent[list->count]->function = func; | |||
list->recurrent[list->count]->user = user; | |||
list->recurrent[list->count]->start_time = get_us(); | |||
list->count++; | |||
return list->count - 1; | |||
} | |||
/* Remove recurrent. Do *NOT* call this from a recurrent function. */ | |||
int remove_recurrent(struct recurrent_list *list, int n) | |||
{ | |||
if (n >= list->count) | |||
return -1; | |||
memmove(&list->recurrent[n], | |||
&list->recurrent[n + 1], | |||
sizeof(struct recurrent *) * (list->count - (n + 1))); | |||
free(list->recurrent[n]); | |||
list->recurrent = (struct recurrent **)realloc(list->recurrent, | |||
sizeof(sizeof | |||
(struct recurrent *)) | |||
* (list->count)); | |||
if (!list->recurrent) | |||
fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, | |||
__LINE__); | |||
list->count--; | |||
return 0; | |||
} |
@@ -0,0 +1,216 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net> | |||
* 2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#include "config.h" | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <stdlib.h> | |||
#include <sys/types.h> | |||
#include <signal.h> | |||
#include <sys/wait.h> | |||
#include <errno.h> | |||
#include <unistd.h> | |||
#include <pwd.h> | |||
#include <caca.h> | |||
#include "neercs.h" | |||
struct screen_list *create_screen_list(void) | |||
{ | |||
struct screen_list *screen_list = NULL; | |||
struct passwd *user_info; | |||
char *user_dir = NULL; | |||
/* Create screen list */ | |||
screen_list = (struct screen_list *)malloc(sizeof(struct screen_list)); | |||
if (!screen_list) | |||
{ | |||
fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, | |||
__LINE__); | |||
return NULL; | |||
} | |||
screen_list->screen = | |||
(struct screen **)malloc(sizeof(sizeof(struct screen *))); | |||
if (!screen_list->screen) | |||
{ | |||
fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, | |||
__LINE__); | |||
free(screen_list); | |||
return NULL; | |||
} | |||
screen_list->count = 0; | |||
screen_list->modals.mini = 1; | |||
screen_list->modals.help = 0; | |||
screen_list->modals.status = 1; | |||
screen_list->modals.window_list = 0; | |||
screen_list->modals.python_command = 0; | |||
screen_list->eyecandy = 1; | |||
screen_list->border_size = 1; | |||
screen_list->title = NULL; | |||
screen_list->wm_type = WM_VSPLIT; | |||
screen_list->in_bell = 0; | |||
screen_list->changed = 0; | |||
screen_list->requested_delay = 0; | |||
screen_list->delay = 1000 / 60; /* Don't refresh more than 60 times | |||
per second */ | |||
screen_list->pty = screen_list->prevpty = 0; | |||
screen_list->dont_update_coords = 0; | |||
screen_list->screensaver.timeout = (60*5) * 1000000; | |||
screen_list->screensaver.data = NULL; | |||
screen_list->screensaver.in_screensaver = 0; | |||
screen_list->lock.autolock_timeout = -1; | |||
screen_list->lock.locked = 0; | |||
screen_list->lock.lock_offset = 0; | |||
screen_list->lock.lock_on_detach = 0; | |||
screen_list->comm.attached = 0; | |||
screen_list->comm.socket[SOCK_SERVER] = 0; | |||
screen_list->comm.socket[SOCK_CLIENT] = 0; | |||
screen_list->comm.socket_dir = NULL; | |||
screen_list->comm.socket_path[SOCK_SERVER] = NULL; | |||
screen_list->comm.socket_path[SOCK_CLIENT] = NULL; | |||
screen_list->comm.session_name = NULL; | |||
screen_list->sys.default_shell = NULL; | |||
screen_list->sys.user_path = NULL; | |||
screen_list->sys.to_grab = NULL; | |||
screen_list->sys.to_start = NULL; | |||
screen_list->sys.nb_to_grab = 0; | |||
screen_list->sys.attach = 0; | |||
screen_list->sys.forceattach = 0; | |||
screen_list->need_refresh = 0; | |||
screen_list->force_refresh = 0; | |||
screen_list->cube.in_switch = 0; | |||
screen_list->cube.duration = 1000000; | |||
screen_list->interpreter_props.box = NULL; | |||
screen_list->recurrent_list = NULL; | |||
screen_list->cv = NULL; | |||
screen_list->dp = NULL; | |||
memset(screen_list->lock.lockmsg, 0, 1024); | |||
memset(screen_list->lock.lockpass, 0, 1024); | |||
/* Build local config file path */ | |||
user_dir = getenv("HOME"); | |||
if (!user_dir) | |||
{ | |||
user_info = getpwuid(getuid()); | |||
if (user_info) | |||
{ | |||
user_dir = user_info->pw_dir; | |||
} | |||
} | |||
if (user_dir) | |||
{ | |||
screen_list->sys.user_path = | |||
malloc(strlen(user_dir) + strlen("/.neercsrc") + 1); | |||
sprintf(screen_list->sys.user_path, "%s/%s", user_dir, ".neercsrc"); | |||
} | |||
screen_list->recurrent_list = | |||
(struct recurrent_list *)malloc(sizeof(struct recurrent_list)); | |||
screen_list->recurrent_list->recurrent = | |||
(struct recurrent **)malloc(sizeof(struct recurrent *)); | |||
if (!screen_list->recurrent_list->recurrent) | |||
{ | |||
fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, | |||
__LINE__); | |||
free(screen_list); | |||
free(screen_list->screen); | |||
return NULL; | |||
} | |||
screen_list->recurrent_list->count = 0; | |||
return screen_list; | |||
} | |||
void free_screen_list(struct screen_list *screen_list) | |||
{ | |||
int i; | |||
struct option *option; | |||
if (screen_list->dp) | |||
caca_free_display(screen_list->dp); | |||
if (screen_list->cv) | |||
caca_free_canvas(screen_list->cv); | |||
for (i = 0; i < screen_list->count; i++) | |||
{ | |||
destroy_screen(screen_list->screen[i]); | |||
} | |||
if (screen_list->comm.socket_path[SOCK_SERVER]) | |||
{ | |||
/* FIXME test that we are the server */ | |||
if (!screen_list->dp) | |||
unlink(screen_list->comm.socket_path[SOCK_SERVER]); | |||
free(screen_list->comm.socket_path[SOCK_SERVER]); | |||
} | |||
if (screen_list->comm.socket_path[SOCK_CLIENT]) | |||
{ | |||
/* FIXME test that we are the client */ | |||
if (screen_list->dp) | |||
unlink(screen_list->comm.socket_path[SOCK_CLIENT]); | |||
free(screen_list->comm.socket_path[SOCK_CLIENT]); | |||
} | |||
if (screen_list->comm.socket[SOCK_CLIENT]) | |||
close(screen_list->comm.socket[SOCK_CLIENT]); | |||
if (screen_list->comm.socket[SOCK_SERVER]) | |||
close(screen_list->comm.socket[SOCK_SERVER]); | |||
if (screen_list->screen) | |||
free(screen_list->screen); | |||
option = screen_list->config; | |||
while (option) | |||
{ | |||
struct option *kromeugnon = option; | |||
option = option->next; | |||
if (kromeugnon->key) | |||
free(kromeugnon->key); | |||
if (kromeugnon->value) | |||
free(kromeugnon->value); | |||
free(kromeugnon); | |||
} | |||
for (i = 0; i < screen_list->recurrent_list->count; i++) | |||
{ | |||
remove_recurrent(screen_list->recurrent_list, i); | |||
i = 0; | |||
} | |||
if (screen_list->recurrent_list->recurrent) | |||
free(screen_list->recurrent_list->recurrent); | |||
if (screen_list->recurrent_list) | |||
free(screen_list->recurrent_list); | |||
if (screen_list->comm.session_name) | |||
free(screen_list->comm.session_name); | |||
if (screen_list->title) | |||
free(screen_list->title); | |||
free(screen_list); | |||
} |
@@ -0,0 +1,334 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net> | |||
* 2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#include "config.h" | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <stdlib.h> | |||
#include <sys/types.h> | |||
#include <signal.h> | |||
#include <sys/wait.h> | |||
#include <errno.h> | |||
#include <unistd.h> | |||
#include <caca.h> | |||
#include "neercs.h" | |||
struct screen *create_screen_grab(int w, int h, int pid) | |||
{ | |||
struct screen *s = (struct screen *)malloc(sizeof(struct screen)); | |||
s->cv = caca_create_canvas(w, h); | |||
caca_set_color_ansi(s->cv, CACA_BLACK, CACA_BLACK); | |||
caca_clear_canvas(s->cv); | |||
s->init = 0; | |||
s->buf = NULL; | |||
s->title = NULL; | |||
s->total = 0; | |||
s->w = w; | |||
s->h = h; | |||
s->bell = 0; | |||
s->report_mouse = MOUSE_NONE; | |||
s->fd = create_pty_grab(pid, w, h, &s->pid); | |||
if (s->fd < 0) | |||
{ | |||
caca_free_canvas(s->cv); | |||
free(s); | |||
return NULL; | |||
} | |||
return s; | |||
} | |||
struct screen *create_screen(int w, int h, char *command) | |||
{ | |||
struct screen *s = (struct screen *)malloc(sizeof(struct screen)); | |||
s->cv = caca_create_canvas(w, h); | |||
caca_set_color_ansi(s->cv, CACA_BLACK, CACA_BLACK); | |||
caca_clear_canvas(s->cv); | |||
s->init = 0; | |||
s->buf = NULL; | |||
s->title = NULL; | |||
s->total = 0; | |||
s->w = w + 1; | |||
s->h = h + 1; | |||
s->bell = 0; | |||
s->visible = 1; | |||
s->scroll = 0; | |||
s->report_mouse = MOUSE_NONE; | |||
s->fd = create_pty(command, w, h, &s->pid); | |||
s->orig_w = s->w; | |||
s->orig_h = s->h; | |||
s->orig_x = s->x; | |||
s->orig_y = s->y; | |||
if (s->fd < 0) | |||
{ | |||
caca_free_canvas(s->cv); | |||
free(s); | |||
return NULL; | |||
} | |||
return s; | |||
} | |||
int destroy_screen(struct screen *s) | |||
{ | |||
if (s->fd >= 0) | |||
close(s->fd); | |||
if (s->buf) | |||
free(s->buf); | |||
if (s->title) | |||
free(s->title); | |||
s->buf = NULL; | |||
if (s->cv) | |||
caca_free_canvas(s->cv); | |||
s->cv = NULL; | |||
free(s); | |||
return 1; | |||
} | |||
int add_screen(struct screen_list *list, struct screen *s) | |||
{ | |||
if (list == NULL || s == NULL) | |||
return -1; | |||
else | |||
{ | |||
list->screen = (struct screen **)realloc(list->screen, | |||
sizeof(sizeof | |||
(struct screen *)) * | |||
(list->count + 1)); | |||
if (!list->screen) | |||
fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, | |||
__LINE__); | |||
list->screen[list->count] = s; | |||
list->count++; | |||
} | |||
list->changed = 1; | |||
return list->count - 1; | |||
} | |||
int remove_screen(struct screen_list *list, int n, int please_kill) | |||
{ | |||
if (n >= list->count) | |||
return -1; | |||
if (please_kill) | |||
{ | |||
int status = 0; | |||
int ret = 0; | |||
/* FIXME */ | |||
close(list->screen[n]->fd); | |||
list->screen[n]->fd = -1; | |||
kill(list->screen[n]->pid, SIGINT); | |||
ret = waitpid(list->screen[n]->pid, &status, | |||
WNOHANG | WUNTRACED | WCONTINUED); | |||
if (!ret) | |||
kill(list->screen[n]->pid, SIGQUIT); | |||
ret = waitpid(list->screen[n]->pid, &status, | |||
WNOHANG | WUNTRACED | WCONTINUED); | |||
if (!ret) | |||
kill(list->screen[n]->pid, SIGABRT); | |||
ret = waitpid(list->screen[n]->pid, &status, | |||
WNOHANG | WUNTRACED | WCONTINUED); | |||
if (!ret) | |||
kill(list->screen[n]->pid, SIGKILL); | |||
} | |||
destroy_screen(list->screen[n]); | |||
memmove(&list->screen[n], | |||
&list->screen[n + 1], | |||
sizeof(struct screen *) * (list->count - (n + 1))); | |||
list->screen = (struct screen **)realloc(list->screen, | |||
sizeof(sizeof(struct screen *)) | |||
* (list->count)); | |||
if (!list->screen) | |||
fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, | |||
__LINE__); | |||
list->count--; | |||
list->changed = 1; | |||
return 1; | |||
} | |||
void refresh_screens(struct screen_list *screen_list) | |||
{ | |||
int i; | |||
if (!screen_list->count) | |||
return; | |||
screen_list->width = caca_get_canvas_width(screen_list->cv); | |||
screen_list->height = | |||
caca_get_canvas_height(screen_list->cv) - (screen_list->modals.mini * 6) - | |||
screen_list->modals.status; | |||
if (!screen_list->dont_update_coords) | |||
{ | |||
update_windows_props(screen_list); | |||
} | |||
if (screen_list->changed) | |||
{ | |||
caca_set_color_ansi(screen_list->cv, CACA_DEFAULT, CACA_DEFAULT); | |||
caca_clear_canvas(screen_list->cv); | |||
} | |||
wm_refresh(screen_list); | |||
caca_gotoxy(screen_list->cv, | |||
screen_list->screen[screen_list->pty]->x + | |||
caca_get_cursor_x(screen_list->screen[screen_list->pty]->cv), | |||
screen_list->screen[screen_list->pty]->y + | |||
caca_get_cursor_y(screen_list->screen[screen_list->pty]->cv)); | |||
if (screen_list->modals.mini) | |||
{ | |||
draw_thumbnails(screen_list); | |||
} | |||
if (screen_list->modals.status) | |||
{ | |||
draw_status(screen_list); | |||
} | |||
if (screen_list->modals.help) | |||
{ | |||
draw_help(screen_list); | |||
} | |||
if (screen_list->modals.window_list) | |||
{ | |||
draw_list(screen_list); | |||
} | |||
#ifdef USE_PYTHON | |||
debug("py : command is %d (at %p)\n", screen_list->modals.python_command, &screen_list->modals.python_command); | |||
if(screen_list->modals.python_command) | |||
{ | |||
draw_python_command(screen_list); | |||
} | |||
#endif | |||
screen_list->changed = 0; | |||
for (i = screen_list->count - 1; i >= 0; i--) | |||
screen_list->screen[i]->changed = 0; | |||
} | |||
int update_screens_contents(struct screen_list *screen_list) | |||
{ | |||
int i, refresh = 0; | |||
int maxfd = 0; | |||
struct timeval tv; | |||
fd_set fdset; | |||
int ret; | |||
/* Read data, if any */ | |||
FD_ZERO(&fdset); | |||
for (i = 0; i < screen_list->count; i++) | |||
{ | |||
if (screen_list->screen[i]->fd >= 0) | |||
FD_SET(screen_list->screen[i]->fd, &fdset); | |||
if (screen_list->screen[i]->fd > maxfd) | |||
maxfd = screen_list->screen[i]->fd; | |||
} | |||
tv.tv_sec = 0; | |||
tv.tv_usec = 50000; | |||
ret = select(maxfd + 1, &fdset, NULL, NULL, &tv); | |||
if (ret < 0) | |||
{ | |||
if (errno == EINTR) | |||
; /* We probably got a SIGWINCH, ignore it */ | |||
else | |||
{ | |||
/* FIXME: Useless since break will mean that we return 0 But I | |||
don't know what was the purpose of this code */ | |||
for (i = 0; i < screen_list->count; i++) | |||
if (screen_list->screen[i]->total) | |||
break; | |||
if (i == screen_list->count) | |||
return 0; | |||
} | |||
} | |||
else if (ret) | |||
{ | |||
for (i = 0; i < screen_list->count; i++) | |||
{ | |||
/* FIXME: try a new strategy: read all filedescriptors until each | |||
of them starved at least once. */ | |||
if (screen_list->screen[i]->fd < 0 || | |||
!FD_ISSET(screen_list->screen[i]->fd, &fdset)) | |||
continue; | |||
for (;;) | |||
{ | |||
ssize_t nr; | |||
screen_list->screen[i]->buf = | |||
realloc(screen_list->screen[i]->buf, | |||
screen_list->screen[i]->total + 1024); | |||
if (!screen_list->screen[i]->buf) | |||
fprintf(stderr, "Can't allocate memory at %s:%d\n", | |||
__FUNCTION__, __LINE__); | |||
nr = read(screen_list->screen[i]->fd, | |||
screen_list->screen[i]->buf + | |||
screen_list->screen[i]->total, 1024); | |||
if (nr > 0) | |||
{ | |||
screen_list->screen[i]->total += nr; | |||
continue; | |||
} | |||
if (nr == 0 || errno != EWOULDBLOCK) | |||
{ | |||
remove_screen(screen_list, i, 0); | |||
if (i < screen_list->prevpty) | |||
screen_list->prevpty--; | |||
if (i == screen_list->pty) | |||
{ | |||
screen_list->pty = screen_list->prevpty; | |||
screen_list->prevpty = 0; | |||
} | |||
if (i < (screen_list->pty)) | |||
(screen_list->pty)--; | |||
/* Don't skip the element which is now at position i */ | |||
i--; | |||
refresh = 1; | |||
} | |||
break; | |||
} | |||
} | |||
} | |||
return refresh; | |||
} |
@@ -0,0 +1,187 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net> | |||
* 2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#include "config.h" | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <stdlib.h> | |||
#include <sys/types.h> | |||
#include <signal.h> | |||
#include <sys/wait.h> | |||
#include <errno.h> | |||
#include <unistd.h> | |||
#include <caca.h> | |||
#include "neercs.h" | |||
void screensaver_init(struct screen_list *screen_list) | |||
{ | |||
screensaver_flying_toasters_init(screen_list); | |||
} | |||
void screensaver_kill(struct screen_list *screen_list) | |||
{ | |||
screensaver_flying_toasters_kill(screen_list); | |||
} | |||
void draw_screensaver(struct screen_list *screen_list) | |||
{ | |||
screensaver_flying_toasters(screen_list); | |||
} | |||
/* Flying Toasters */ | |||
#define COUNT 15 | |||
#define PRECISION 100 | |||
char toaster_text[3][99] = { { | |||
" __._ \n" | |||
" .-'== _',\n" | |||
" <|_= .-' |\n" | |||
" | --| \\'.-_ \n" | |||
" | | \\ \" _.\n" | |||
" `-_|.-\\_.-\n"}, | |||
{ | |||
" __._ \n" | |||
" .-'== _',\n" | |||
" \\|_= .-' |\n" | |||
" | --| __'-.\n" | |||
" | | ___.-\n" | |||
" `-_|.-\n"}, | |||
{ | |||
" _- __._\n" | |||
" /.-'== _',_.-.\n" | |||
" \\|_= .-'/ _.'\n" | |||
" | --| / .-\n" | |||
" | | _.|\n" | |||
" `-_|.-\n"} | |||
}; | |||
char toaster_mask[3][99] = { { | |||
" __._ \n" | |||
" .-'== _',\n" | |||
" <|_=X.-'XX|\n" | |||
" |X--|XXX\\'.-_ \n" | |||
" |XXX|X\\XX\"X_.\n" | |||
" `-_|.-\\_.-\n"}, | |||
{ | |||
" __._ \n" | |||
" .-'== _',\n" | |||
" \\|_= .-'XX|\n" | |||
" |X--|XX__'-.\n" | |||
" |XXX|X___.-\n" | |||
" `-_|.-\n"}, | |||
{ | |||
" _- __._\n" | |||
" /.-'== _',_.-.\n" | |||
" \\|_= .-'/XX_.'\n" | |||
" |X--|X/X.-\n" | |||
" |XXX|XX_.|\n" | |||
" `-_|.-\n"} | |||
}; | |||
struct flying_toaster | |||
{ | |||
int x[COUNT], y[COUNT], s[COUNT]; | |||
caca_canvas_t **toaster; | |||
caca_canvas_t **mask; | |||
}; | |||
void screensaver_flying_toasters_init(struct screen_list *screen_list) | |||
{ | |||
struct flying_toaster *flying_toaster; | |||
int w = caca_get_canvas_width(screen_list->cv); | |||
int h = caca_get_canvas_height(screen_list->cv); | |||
int i; | |||
flying_toaster = malloc(sizeof(struct flying_toaster)); | |||
flying_toaster->toaster = | |||
(caca_canvas_t **) malloc(sizeof(caca_canvas_t *) * 3); | |||
flying_toaster->mask = | |||
(caca_canvas_t **) malloc(sizeof(caca_canvas_t *) * 3); | |||
for (i = 0; i < 3; i++) | |||
{ | |||
flying_toaster->toaster[i] = caca_create_canvas(0, 0); | |||
flying_toaster->mask[i] = caca_create_canvas(0, 0); | |||
caca_import_canvas_from_memory(flying_toaster->toaster[i], | |||
toaster_text[i], | |||
strlen(toaster_text[i]), "utf8"); | |||
caca_import_canvas_from_memory(flying_toaster->mask[i], | |||
toaster_mask[i], | |||
strlen(toaster_mask[i]), "utf8"); | |||
} | |||
for (i = 0; i < COUNT; i++) | |||
{ | |||
flying_toaster->x[i] = (rand() % w) * PRECISION; | |||
flying_toaster->y[i] = (rand() % h) * PRECISION; | |||
flying_toaster->s[i] = (rand() % 3) * PRECISION; | |||
} | |||
screen_list->screensaver.data = flying_toaster; | |||
} | |||
void screensaver_flying_toasters_kill(struct screen_list *screen_list) | |||
{ | |||
struct flying_toaster *flying_toaster = screen_list->screensaver.data; | |||
caca_free_canvas(flying_toaster->toaster[0]); | |||
caca_free_canvas(flying_toaster->toaster[1]); | |||
caca_free_canvas(flying_toaster->toaster[2]); | |||
free(flying_toaster->toaster); | |||
free(flying_toaster); | |||
screen_list->screensaver.data = NULL; | |||
} | |||
void screensaver_flying_toasters(struct screen_list *screen_list) | |||
{ | |||
struct flying_toaster *d = screen_list->screensaver.data; | |||
int i, w, h, x, y, s; | |||
if (!d) | |||
return; | |||
w = caca_get_canvas_width(screen_list->cv); | |||
h = caca_get_canvas_height(screen_list->cv); | |||
caca_set_color_ansi(screen_list->cv, CACA_WHITE, CACA_BLACK); | |||
caca_clear_canvas(screen_list->cv); | |||
for (i = 0; i < COUNT; i++) | |||
{ | |||
x = d->x[i] / PRECISION; | |||
y = d->y[i] / PRECISION; | |||
s = d->s[i] / PRECISION; | |||
caca_blit(screen_list->cv, x, y, d->toaster[s], d->mask[s]); | |||
d->x[i] -= 40; | |||
d->y[i] += 10; | |||
if (d->x[i] / PRECISION + caca_get_canvas_width(d->toaster[s]) <= 0) | |||
d->x[i] = ((rand() % w) / 3 + w) * PRECISION; | |||
if (d->y[i] / PRECISION >= h) | |||
d->y[i] = ((rand() % h) / 2 - h) * PRECISION; | |||
d->s[i] = ((d->s[i] + 24) % (3 * PRECISION)); | |||
} | |||
} |
@@ -0,0 +1,680 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net> | |||
* 2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* 2008-2010 Pascal Terjan <pterjan@linuxfr.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#include "config.h" | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <stdlib.h> | |||
#include <unistd.h> | |||
#include <fcntl.h> | |||
#include <signal.h> | |||
#include <sys/ioctl.h> | |||
#include <sys/socket.h> | |||
#include <sys/types.h> | |||
#include <sys/wait.h> | |||
#include <sys/time.h> | |||
#include <time.h> | |||
#include <pwd.h> | |||
#include <errno.h> | |||
#include <caca.h> | |||
#include "neercs.h" | |||
static void server_init(struct screen_list *screen_list); | |||
static void refresh_screen(struct screen_list *screen_list, int refresh); | |||
static int handle_key(struct screen_list *screen_list, unsigned int c, | |||
int refresh); | |||
static int handle_attach(struct screen_list *screen_list, char *buf); | |||
static int send_to_client(const char *msg, int size, | |||
struct screen_list *screen_list) | |||
{ | |||
int ret; | |||
if (!screen_list->comm.socket[SOCK_CLIENT]) | |||
connect_socket(screen_list, SOCK_CLIENT); | |||
if (!screen_list->comm.socket[SOCK_CLIENT]) | |||
ret = -1; | |||
else | |||
ret = write(screen_list->comm.socket[SOCK_CLIENT], msg, size); | |||
if (ret < 0 && errno != EAGAIN) | |||
{ | |||
fprintf(stderr, "Failed to send message to client: %s\n", | |||
strerror(errno)); | |||
if (screen_list->comm.attached) | |||
detach(screen_list); | |||
} | |||
return ret; | |||
} | |||
static int set_title(struct screen_list *screen_list) | |||
{ | |||
char buf[1024]; | |||
int bytes; | |||
char *title = NULL; | |||
if (screen_list->comm.attached) | |||
{ | |||
if (screen_list->pty < screen_list->count && | |||
screen_list->screen[screen_list->pty]->title) | |||
title = screen_list->screen[screen_list->pty]->title; | |||
else | |||
title = PACKAGE_STRING; | |||
} | |||
if (screen_list->title) | |||
{ | |||
if (!strcmp(screen_list->title, title)) | |||
return 0; | |||
free(screen_list->title); | |||
} | |||
screen_list->title = strdup(title); | |||
bytes = snprintf(buf, sizeof(buf) - 1, "TITLE %s", title); | |||
buf[bytes] = '\0'; | |||
return send_to_client(buf, strlen(buf), screen_list); | |||
} | |||
static int set_cursor(int state, struct screen_list *screen_list) | |||
{ | |||
char buf[16]; | |||
int bytes; | |||
bytes = snprintf(buf, sizeof(buf) - 1, "CURSOR %d", state); | |||
buf[bytes] = '\0'; | |||
return send_to_client(buf, strlen(buf), screen_list); | |||
} | |||
static int request_refresh(struct screen_list *screen_list) | |||
{ | |||
int ndirty = caca_get_dirty_rect_count(screen_list->cv); | |||
if (!ndirty) | |||
return 0; | |||
if (!screen_list->comm.socket[SOCK_CLIENT]) | |||
connect_socket(screen_list, SOCK_CLIENT); | |||
if (screen_list->comm.socket[SOCK_CLIENT]) | |||
{ | |||
size_t bufsize, towrite; | |||
ssize_t written, ret; | |||
socklen_t optlen = sizeof(bufsize); | |||
size_t bytes; | |||
void *buf; | |||
char buf2[32]; | |||
int x, y, i; | |||
getsockopt(screen_list->comm.socket[SOCK_CLIENT], SOL_SOCKET, | |||
SO_SNDBUF, &bufsize, &optlen); | |||
bufsize /= 2; | |||
debug("bufsize=%d", bufsize); | |||
for (i = 0; i < ndirty; i++) | |||
{ | |||
int w, h; | |||
caca_get_dirty_rect(screen_list->cv, i, &x, &y, &w, &h); | |||
debug("dirty @%d,%d %dx%d [%dx%d]", x, y, w, h, | |||
caca_get_canvas_width(screen_list->cv), | |||
caca_get_canvas_height(screen_list->cv)); | |||
buf = | |||
caca_export_area_to_memory(screen_list->cv, x, y, w, h, "caca", | |||
&bytes); | |||
debug("Requesting refresh for %d", bytes); | |||
towrite = bytes; | |||
written = 0; | |||
sprintf(buf2, "UPDATE %10d %10d", x, y); | |||
ret = send_to_client(buf2, strlen(buf2) + 1, screen_list); | |||
if (ret < 29) | |||
{ | |||
free(buf); | |||
return -1; | |||
} | |||
/* Block to write the end of the message */ | |||
fcntl(screen_list->comm.socket[SOCK_CLIENT], F_SETFL, 0); | |||
while (towrite > 0) | |||
{ | |||
ssize_t n; | |||
debug("Wrote %d, %d remaining", written, towrite); | |||
n = send_to_client((char *)buf + written, | |||
towrite > bufsize ? bufsize : towrite, | |||
screen_list); | |||
if (n < 0) | |||
{ | |||
debug("Can't refresh (%s), with %d bytes (out of %d)", | |||
strerror(errno), | |||
towrite > bufsize ? bufsize : towrite, towrite); | |||
return -1; | |||
} | |||
written += n; | |||
towrite -= n; | |||
} | |||
fcntl(screen_list->comm.socket[SOCK_CLIENT], F_SETFL, O_NONBLOCK); | |||
free(buf); | |||
} | |||
sprintf(buf2, "REFRESH %10d %10d", caca_get_cursor_x(screen_list->cv), | |||
caca_get_cursor_y(screen_list->cv)); | |||
/* FIXME check value of r */ | |||
int r = send_to_client(buf2, strlen(buf2) + 1, screen_list); | |||
(void)r; | |||
caca_clear_dirty_rect_list(screen_list->cv); | |||
} | |||
return 0; | |||
} | |||
int detach(struct screen_list *screen_list) | |||
{ | |||
screen_list->comm.attached = 0; | |||
if (screen_list->lock.lock_on_detach) | |||
screen_list->lock.locked = 1; | |||
if (screen_list->comm.socket[SOCK_CLIENT]) | |||
{ | |||
send_to_client("DETACH", 6, screen_list); | |||
close(screen_list->comm.socket[SOCK_CLIENT]); | |||
screen_list->comm.socket[SOCK_CLIENT] = 0; | |||
} | |||
return 0; | |||
} | |||
static int server_iteration(struct screen_list *screen_list) | |||
{ | |||
int i; | |||
int eof = 0, refresh; | |||
int quit = 0; | |||
ssize_t n; | |||
char buf[128]; | |||
/* Read program output */ | |||
refresh = update_screens_contents(screen_list); | |||
/* Check if we got something from the client */ | |||
while (screen_list->comm.socket[SOCK_SERVER] | |||
&& (n = | |||
read(screen_list->comm.socket[SOCK_SERVER], buf, | |||
sizeof(buf) - 1)) > 0) | |||
{ | |||
buf[n] = 0; | |||
debug("Received command %s", buf); | |||
if (!strncmp("ATTACH ", buf, 7)) | |||
{ | |||
refresh |= handle_attach(screen_list, buf); | |||
} | |||
else if (!strncmp("QUIT", buf, 4)) | |||
{ | |||
quit = 1; | |||
} | |||
else if (!strncmp("DELAY ", buf, 6)) | |||
{ | |||
/* FIXME check the length before calling atoi */ | |||
screen_list->delay = atoi(buf + 6); | |||
} | |||
else if (!strncmp("RESIZE ", buf, 7)) | |||
{ | |||
caca_free_canvas(screen_list->cv); | |||
/* FIXME check the length before calling atoi */ | |||
screen_list->cv = | |||
caca_create_canvas(atoi(buf + 7), atoi(buf + 18)); | |||
screen_list->changed = 1; | |||
refresh = 1; | |||
} | |||
else if (!strncmp("KEY ", buf, 4)) | |||
{ | |||
unsigned int c = atoi(buf + 4); | |||
refresh |= handle_key(screen_list, c, refresh); | |||
} | |||
else if (!strncmp("MOUSEP ", buf, 6)) | |||
{ | |||
debug("Got mouse press '%s'\n", buf); | |||
int x, y, b; | |||
x = 1 + atoi(buf + 7) - screen_list->screen[screen_list->pty]->x; | |||
y = 1 + atoi(buf + 18) - screen_list->screen[screen_list->pty]->y; | |||
b = atoi(buf + 28); | |||
switch (screen_list->screen[screen_list->pty]->report_mouse) | |||
{ | |||
case MOUSE_VT200: | |||
case MOUSE_VT200_HIGHLIGHT: | |||
case MOUSE_BTN_EVENT: | |||
case MOUSE_ANY_EVENT: | |||
sprintf(buf, "\x1b[M%c%c%c", 32 + (b - 1), x + 32, y + 32); | |||
debug("mousea send ESC[M %d %d %d", (b - 1), x + 32, y + 32); | |||
send_ansi_sequence(screen_list, buf); | |||
break; | |||
case MOUSE_X10: | |||
sprintf(buf, "\x1b[M%c%c%c", 32 + (b - 1), 32 + x, 32 + y); | |||
debug("mousex send ESC[M %d %d %d", 32 + (b - 1), 32 + x, | |||
32 + y); | |||
send_ansi_sequence(screen_list, buf); | |||
break; | |||
case MOUSE_NONE: | |||
break; | |||
} | |||
} | |||
else if (!strncmp("MOUSER ", buf, 6)) | |||
{ | |||
debug("Got mouse release '%s'\n", buf); | |||
int x, y, b; | |||
x = 1 + atoi(buf + 7) - screen_list->screen[screen_list->pty]->x; | |||
y = 1 + atoi(buf + 18) - screen_list->screen[screen_list->pty]->y; | |||
b = atoi(buf + 28); | |||
switch (screen_list->screen[screen_list->pty]->report_mouse) | |||
{ | |||
case MOUSE_VT200: | |||
case MOUSE_VT200_HIGHLIGHT: | |||
case MOUSE_BTN_EVENT: | |||
case MOUSE_ANY_EVENT: | |||
sprintf(buf, "\x1b[M%c%c%c", 32 + 3, x + 32, y + 32); | |||
send_ansi_sequence(screen_list, buf); | |||
break; | |||
case MOUSE_X10: | |||
sprintf(buf, "\x1b[M%c%c%c", 32 + 3, 32 + x, 32 + y); | |||
send_ansi_sequence(screen_list, buf); | |||
break; | |||
case MOUSE_NONE: | |||
break; | |||
} | |||
} | |||
else if (!strncmp("MOUSEM ", buf, 6)) | |||
{ | |||
debug("Got mouse motion '%s'\n", buf); | |||
int x, y, b; | |||
x = 1 + atoi(buf + 7) - screen_list->screen[screen_list->pty]->x; | |||
y = 1 + atoi(buf + 18) - screen_list->screen[screen_list->pty]->y; | |||
b = atoi(buf + 28); | |||
switch (screen_list->screen[screen_list->pty]->report_mouse) | |||
{ | |||
case MOUSE_X10: // X10 reports mouse clicks only | |||
if (b) | |||
{ | |||
sprintf(buf, "\x1b[M%c%c%c", 32 + (b - 1), x + 32, y + 32); | |||
send_ansi_sequence(screen_list, buf); | |||
} | |||
break; | |||
case MOUSE_VT200: | |||
case MOUSE_VT200_HIGHLIGHT: | |||
case MOUSE_BTN_EVENT: | |||
if (b) | |||
{ | |||
sprintf(buf, "\x1b[M%c%c%c", 32 + (b - 1), x + 32, y + 32); | |||
send_ansi_sequence(screen_list, buf); | |||
} | |||
case MOUSE_ANY_EVENT: | |||
sprintf(buf, "\x1b[M%c%c%c", 32 + (b - 1), x + 32, y + 32); | |||
send_ansi_sequence(screen_list, buf); | |||
break; | |||
case MOUSE_NONE: | |||
break; | |||
} | |||
} | |||
else | |||
{ | |||
fprintf(stderr, "Unknown command received: %s\n", buf); | |||
} | |||
} | |||
/* No more screens, exit */ | |||
if (!screen_list->count) | |||
return -1; | |||
/* User requested to exit */ | |||
if (quit) | |||
return -2; | |||
/* Update each screen canvas */ | |||
refresh |= update_terms(screen_list); | |||
/* Launch recurrents if any */ | |||
refresh |= handle_recurrents(screen_list); | |||
/* Refresh screen */ | |||
refresh_screen(screen_list, refresh); | |||
eof = 1; | |||
for (i = 0; i < screen_list->count; i++) | |||
if (screen_list->screen[i]->fd >= 0) | |||
eof = 0; | |||
if (eof) | |||
return -3; | |||
return 0; | |||
} | |||
static void server_main(struct screen_list *screen_list) | |||
{ | |||
screen_list->last_key_time = 0; | |||
screen_list->comm.attached = 0; | |||
screen_list->command = 0; | |||
screen_list->was_in_bell = 0; | |||
screen_list->last_refresh_time = 0; | |||
server_init(screen_list); | |||
#ifdef USE_PYTHON | |||
python_init(screen_list); | |||
#endif | |||
for (;;) | |||
{ | |||
if (server_iteration(screen_list)) | |||
break; | |||
} | |||
detach(screen_list); | |||
free_screen_list(screen_list); | |||
#ifdef USE_PYTHON | |||
python_close(screen_list); | |||
#endif | |||
exit(0); | |||
} | |||
static void refresh_screen(struct screen_list *screen_list, int refresh) | |||
{ | |||
if (!screen_list->comm.attached) | |||
{ | |||
/* No need to refresh Don't use the CPU too much Would be better to | |||
select on terms + socket */ | |||
sleep(1); | |||
} | |||
else | |||
{ | |||
long long unsigned int current_time = get_us(); | |||
long long int tdiff = | |||
(current_time - screen_list->last_refresh_time) / 1000; | |||
refresh |= screen_list->need_refresh; | |||
if (screen_list->force_refresh) | |||
{ | |||
wm_refresh(screen_list); | |||
refresh = 1; | |||
} | |||
/* Draw lock window */ | |||
if (screen_list->lock.locked) | |||
{ | |||
/* FIXME don't redraw it each iteration */ | |||
draw_lock(screen_list); | |||
refresh = 1; | |||
} | |||
#ifdef USE_LOCK | |||
else if ((current_time - screen_list->last_key_time >= | |||
screen_list->lock.autolock_timeout)) | |||
{ | |||
screen_list->lock.locked = 1; | |||
refresh = 1; | |||
} | |||
#endif | |||
else if ((current_time - screen_list->last_key_time >= | |||
screen_list->screensaver.timeout)) | |||
{ | |||
if (!screen_list->screensaver.in_screensaver) | |||
{ | |||
screensaver_init(screen_list); | |||
screen_list->screensaver.in_screensaver = 1; | |||
set_cursor(0, screen_list); | |||
} | |||
draw_screensaver(screen_list); | |||
refresh = 1; | |||
} | |||
else if (refresh || screen_list->was_in_bell) | |||
{ | |||
if (tdiff >= screen_list->delay) | |||
{ | |||
refresh_screens(screen_list); | |||
set_title(screen_list); | |||
refresh = 1; | |||
} | |||
} | |||
if (refresh) | |||
{ | |||
if (tdiff >= screen_list->delay) | |||
{ | |||
request_refresh(screen_list); | |||
screen_list->last_refresh_time = current_time; | |||
screen_list->need_refresh = 0; | |||
} | |||
else | |||
{ | |||
debug("Skipping refresh (%lld < %d)", tdiff, | |||
screen_list->delay); | |||
screen_list->need_refresh = 1; | |||
} | |||
} | |||
} | |||
} | |||
static void server_init(struct screen_list *screen_list) | |||
{ | |||
int i; | |||
debug("Screen list at %p\n", screen_list); | |||
/* Create socket and bind it */ | |||
create_socket(screen_list, SOCK_SERVER); | |||
/* Connect to the client */ | |||
connect_socket(screen_list, SOCK_CLIENT); | |||
screen_list->width = screen_list->height = 80; | |||
/* Create main canvas */ | |||
screen_list->cv = caca_create_canvas(screen_list->width, | |||
screen_list->height | |||
+ screen_list->modals.mini * 6 | |||
+ screen_list->modals.status); | |||
if (!screen_list->sys.to_grab && !screen_list->sys.to_start) | |||
{ | |||
add_screen(screen_list, | |||
create_screen(screen_list->width, | |||
screen_list->height, | |||
screen_list->sys.default_shell)); | |||
} | |||
/* Attach processes */ | |||
if (screen_list->sys.to_grab) | |||
{ | |||
for (i = 0; screen_list->sys.to_grab[i]; i++) | |||
{ | |||
add_screen(screen_list, | |||
create_screen_grab(screen_list->width, | |||
screen_list->height, | |||
screen_list->sys.to_grab[i])); | |||
} | |||
free(screen_list->sys.to_grab); | |||
screen_list->sys.to_grab = NULL; | |||
} | |||
/* Launch command line processes */ | |||
if (screen_list->sys.to_start) | |||
{ | |||
for (i = 0; screen_list->sys.to_start[i]; i++) | |||
{ | |||
add_screen(screen_list, | |||
create_screen(screen_list->width, | |||
screen_list->height, | |||
screen_list->sys.to_start[i])); | |||
free(screen_list->sys.to_start[i]); | |||
} | |||
free(screen_list->sys.to_start); | |||
screen_list->sys.to_start = NULL; | |||
} | |||
screen_list->last_key_time = get_us(); | |||
} | |||
static int handle_attach(struct screen_list *screen_list, char *buf) | |||
{ | |||
/* If we were attached to someone else, detach first */ | |||
if (screen_list->comm.attached) | |||
detach(screen_list); | |||
screen_list->comm.attached = 1; | |||
caca_free_canvas(screen_list->cv); | |||
screen_list->cv = caca_create_canvas(atoi(buf + 7), atoi(buf + 18)); | |||
screen_list->delay = atoi(buf + 29); | |||
screen_list->changed = 1; | |||
return 1; | |||
} | |||
static int handle_key(struct screen_list *screen_list, unsigned int c, | |||
int refresh) | |||
{ | |||
char *str = NULL; | |||
int size = 0; | |||
if (screen_list->modals.help) | |||
{ | |||
return help_handle_key(screen_list, c); | |||
} | |||
#ifdef USE_PYTHON | |||
if (screen_list->modals.python_command) | |||
{ | |||
return python_command_handle_key(screen_list, c); | |||
} | |||
#endif | |||
/* CTRL-A has been pressed before, handle this as a command, except that | |||
CTRL-A a sends literal CTRL-A */ | |||
if (screen_list->command && (c != 'a')) | |||
{ | |||
screen_list->command = 0; | |||
refresh |= handle_command_input(screen_list, c); | |||
} | |||
else | |||
{ | |||
/* Not in command mode */ | |||
screen_list->last_key_time = get_us(); | |||
set_cursor(1, screen_list); | |||
/* Kill screensaver */ | |||
if (screen_list->screensaver.in_screensaver) | |||
{ | |||
screensaver_kill(screen_list); | |||
screen_list->screensaver.in_screensaver = 0; | |||
screen_list->changed = 1; | |||
refresh = 1; | |||
return refresh; | |||
} | |||
/* Handle lock window */ | |||
if (screen_list->lock.locked) | |||
{ | |||
refresh |= update_lock(c, screen_list); | |||
screen_list->changed = 1; | |||
} | |||
else if (screen_list->modals.window_list) | |||
{ | |||
refresh |= update_window_list(c, screen_list); | |||
screen_list->changed = 1; | |||
} | |||
else | |||
{ | |||
switch (c) | |||
{ | |||
case 0x01: // CACA_KEY_CTRL_A: | |||
screen_list->command = 1; | |||
break; | |||
default: | |||
/* CTRL-A a sends literal CTRL-A */ | |||
if (screen_list->command && (c == 'a')) | |||
{ | |||
c = 0x01; | |||
} | |||
/* Normal key, convert it if needed */ | |||
str = convert_input_ansi(&c, &size); | |||
/* FIXME check value of r */ | |||
int r = write(screen_list->screen[screen_list->pty]->fd, str, | |||
size); | |||
(void)r; | |||
break; | |||
} | |||
} | |||
} | |||
return refresh; | |||
} | |||
int send_ansi_sequence(struct screen_list *screen_list, char *str) | |||
{ | |||
debug("Sending ansi '%s'\n", str); | |||
return write(screen_list->screen[screen_list->pty]->fd, str, strlen(str)); | |||
} | |||
int install_fds(struct screen_list *screen_list) | |||
{ | |||
int fd; | |||
close(0); | |||
close(1); | |||
close(2); | |||
fd = open("/dev/null", O_RDWR, 0); | |||
if (fd < 0) | |||
{ | |||
perror("Failed to open /dev/null"); | |||
return -2; | |||
} | |||
dup2(fd, 0); | |||
#ifndef DEBUG | |||
dup2(fd, 1); | |||
dup2(fd, 2); | |||
if (fd > 2) | |||
close(fd); | |||
#else | |||
if (fd != 0) | |||
close(fd); | |||
screen_list->outfd = | |||
open("/tmp/neercs-debug.txt", O_TRUNC | O_RDWR | O_CREAT, | |||
S_IRUSR | S_IWUSR); | |||
dup2(screen_list->outfd, 1); | |||
dup2(screen_list->outfd, 2); | |||
if (screen_list->outfd > 2) | |||
close(screen_list->outfd); | |||
#endif | |||
return 0; | |||
} | |||
int start_server(struct screen_list *screen_list) | |||
{ | |||
pid_t pid; | |||
pid = fork(); | |||
if (pid < 0) | |||
{ | |||
perror("Failed to create child process"); | |||
return -1; | |||
} | |||
if (pid == 0) | |||
{ | |||
int r = install_fds(screen_list); | |||
if (r) | |||
return r; | |||
setsid(); | |||
server_main(screen_list); | |||
/* Never returns */ | |||
} | |||
return 0; | |||
} | |||
long long get_us(void) | |||
{ | |||
struct timeval tv; | |||
gettimeofday(&tv, NULL); | |||
return (tv.tv_sec * (1000000) + tv.tv_usec); | |||
} |
@@ -0,0 +1,133 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#include "config.h" | |||
#define _XOPEN_SOURCE | |||
#include <stdlib.h> | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <sys/ioctl.h> | |||
#include <sys/types.h> | |||
#include <termios.h> | |||
#if defined HAVE_PTY_H | |||
# include <pty.h> /* for openpty and forkpty */ | |||
#elif defined HAVE_UTIL_H | |||
# include <util.h> /* for OS X, OpenBSD and NetBSD */ | |||
#elif defined HAVE_LIBUTIL_H | |||
# include <libutil.h> /* for FreeBSD */ | |||
#endif | |||
#include <unistd.h> | |||
#include <fcntl.h> | |||
#include <caca.h> | |||
#include "neercs.h" | |||
int create_pty(char *cmd, unsigned int w, unsigned int h, int *cpid) | |||
{ | |||
char **argv; | |||
int fd; | |||
pid_t pid; | |||
pid = forkpty(&fd, NULL, NULL, NULL); | |||
if (pid < 0) | |||
{ | |||
fprintf(stderr, "forkpty() error\n"); | |||
return -1; | |||
} | |||
else if (pid == 0) | |||
{ | |||
set_tty_size(0, w, h); | |||
putenv("CACA_DRIVER=slang"); | |||
putenv("TERM=xterm"); | |||
argv = malloc(2 * sizeof(char *)); | |||
if (!argv) | |||
{ | |||
fprintf(stderr, "Can't allocate memory at %s:%d\n", __FUNCTION__, | |||
__LINE__); | |||
return -1; | |||
} | |||
argv[0] = cmd; | |||
argv[1] = NULL; | |||
execvp(cmd, argv); | |||
fprintf(stderr, "execvp() error\n"); | |||
return -1; | |||
} | |||
*cpid = pid; | |||
fcntl(fd, F_SETFL, O_NDELAY); | |||
return fd; | |||
} | |||
int create_pty_grab(long pid, unsigned int w, unsigned int h, int *newpid) | |||
{ | |||
int fdm, fds; | |||
int ret = openpty(&fdm, &fds, NULL, NULL, NULL); | |||
if (ret < 0) | |||
{ | |||
fprintf(stderr, "open() error\n"); | |||
return -1; | |||
} | |||
set_tty_size(0, w, h); | |||
grab_process(pid, ptsname(fdm), fds, newpid); | |||
fcntl(fdm, F_SETFL, O_NDELAY); | |||
return fdm; | |||
} | |||
int set_tty_size(int fd, unsigned int w, unsigned int h) | |||
{ | |||
struct winsize ws; | |||
memset(&ws, 0, sizeof(ws)); | |||
ws.ws_row = h; | |||
ws.ws_col = w; | |||
ioctl(fd, TIOCSWINSZ, (char *)&ws); | |||
return 0; | |||
} | |||
int update_terms(struct screen_list *screen_list) | |||
{ | |||
int i, refresh = 0; | |||
for (i = 0; i < screen_list->count; i++) | |||
{ | |||
if (screen_list->screen[i]->total && !screen_list->dont_update_coords) | |||
{ | |||
unsigned long int bytes; | |||
bytes = import_term(screen_list, | |||
screen_list->screen[i], | |||
screen_list->screen[i]->buf, | |||
screen_list->screen[i]->total); | |||
if (bytes > 0) | |||
{ | |||
screen_list->screen[i]->total -= bytes; | |||
memmove(screen_list->screen[i]->buf, | |||
screen_list->screen[i]->buf + bytes, | |||
screen_list->screen[i]->total); | |||
if (screen_list->screen[i]->visible || screen_list->modals.mini) | |||
refresh = 1; | |||
} | |||
} | |||
} | |||
return refresh; | |||
} |
@@ -0,0 +1,176 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2009-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#include "widgets.h" | |||
static void widget_ibox_add_char(struct input_box *box, unsigned int c); | |||
static void widget_ibox_del_char(struct input_box *box); | |||
struct input_box *widget_ibox_init(caca_canvas_t *cv, int w, int h) | |||
{ | |||
struct input_box *box = malloc(sizeof(struct input_box)); | |||
if (!box) | |||
return NULL; | |||
box->cv = cv; | |||
box->w = w; | |||
box->h = h; | |||
box->command = NULL; | |||
box->output_err = NULL; | |||
box->output_res = NULL; | |||
return box; | |||
} | |||
int widget_ibox_draw(struct input_box *box) | |||
{ | |||
int x = (caca_get_canvas_width(box->cv) - box->w) / 2; | |||
int y = (caca_get_canvas_height(box->cv) - box->h) / 2; | |||
caca_set_color_ansi(box->cv, CACA_BLUE, CACA_BLUE); | |||
caca_fill_box(box->cv, x, y, box->w, box->h, '#'); | |||
caca_set_color_ansi(box->cv, CACA_DEFAULT, CACA_BLUE); | |||
caca_draw_cp437_box(box->cv, x, y, box->w, box->h); | |||
caca_printf(box->cv, x, y, "Mini-command"); | |||
caca_printf(box->cv, x + 2, y + 2, | |||
"[___________________________________________________________]"); | |||
if (box->command) | |||
{ | |||
caca_printf(box->cv, x + 3, y + 2, "%s", box->command); | |||
caca_gotoxy(box->cv, x + 3 + box->x, y + 2); | |||
} | |||
else | |||
{ | |||
caca_gotoxy(box->cv, x + 3, y + 2); | |||
} | |||
if (box->output_err) | |||
{ | |||
caca_set_color_ansi(box->cv, CACA_RED, CACA_BLUE); | |||
caca_printf(box->cv, x + 2, y + 4, box->output_err); | |||
} | |||
if (box->output_res) | |||
{ | |||
caca_set_color_ansi(box->cv, CACA_LIGHTGREEN, CACA_BLUE); | |||
caca_printf(box->cv, x + 2, y + 4, box->output_res); | |||
} | |||
return 0; | |||
} | |||
char *widget_ibox_get_text(struct input_box *box) | |||
{ | |||
return box->command; | |||
} | |||
void widget_ibox_destroy(struct input_box *box) | |||
{ | |||
if(!box) return; | |||
if (box->command) | |||
free(box->command); | |||
if (box->output_err) | |||
free(box->output_err); | |||
if (box->output_res) | |||
free(box->output_res); | |||
} | |||
void widget_ibox_set_error(struct input_box *box, char *err) | |||
{ | |||
box->output_err = err; | |||
} | |||
void widget_ibox_set_msg(struct input_box *box, char *msg) | |||
{ | |||
box->output_res = msg; | |||
} | |||
int widget_ibox_handle_key(struct input_box *box, unsigned int c) | |||
{ | |||
if (c == CACA_KEY_ESCAPE) | |||
{ | |||
if (box->command) | |||
{ | |||
free(box->command); | |||
box->command = NULL; | |||
} | |||
return INPUT_BOX_ESC; | |||
} | |||
else if (c == CACA_KEY_LEFT) | |||
{ | |||
if (box->x) | |||
box->x--; | |||
} | |||
else if (c == CACA_KEY_RIGHT) | |||
{ | |||
if (box->x < box->size - 1) | |||
box->x++; | |||
} | |||
else if (c == CACA_KEY_RETURN) | |||
{ | |||
return INPUT_BOX_RET; | |||
} | |||
else | |||
{ | |||
if (c >= ' ' && c < 127) | |||
widget_ibox_add_char(box, c); | |||
else if (c == 8) | |||
{ | |||
widget_ibox_del_char(box); | |||
} | |||
} | |||
return INPUT_BOX_NOTHING; | |||
} | |||
static void widget_ibox_add_char(struct input_box *box, unsigned int c) | |||
{ | |||
/* FIXME handle return values */ | |||
if (!box->command) | |||
{ | |||
box->size = 1; | |||
box->x = 0; | |||
box->command = (char *)malloc(2); | |||
box->command[0] = 0; | |||
} | |||
else | |||
{ | |||
box->command = (char *)realloc(box->command, box->size + 1); | |||
} | |||
memmove(&box->command[box->x + 1], | |||
&box->command[box->x], (box->size - box->x)); | |||
box->command[box->x] = c; | |||
box->x++; | |||
box->size++; | |||
} | |||
static void widget_ibox_del_char(struct input_box *box) | |||
{ | |||
if (box->x < 1) | |||
return; | |||
if (box->size > 1) | |||
box->size--; | |||
else | |||
return; | |||
memcpy(&box->command[box->x - 1], &box->command[box->x], box->size - box->x); | |||
box->command = (char *)realloc(box->command, box->size); | |||
if (box->x) | |||
box->x--; | |||
box->command[box->size - 1] = 0; | |||
} |
@@ -0,0 +1,47 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2009-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#include "config.h" | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <stdlib.h> | |||
#include <sys/types.h> | |||
#include <caca.h> | |||
enum input_box_code | |||
{ | |||
INPUT_BOX_ESC, | |||
INPUT_BOX_RET, | |||
INPUT_BOX_NOTHING, | |||
}; | |||
struct input_box | |||
{ | |||
caca_canvas_t *cv; | |||
int x, y; | |||
int w, h; | |||
int size; | |||
char *command; | |||
char *output_err; | |||
char *output_res; | |||
}; | |||
struct input_box *widget_ibox_init(caca_canvas_t * cv, int w, int h); | |||
int widget_ibox_draw(struct input_box *box); | |||
int widget_ibox_handle_key(struct input_box *box, unsigned int c); | |||
char* widget_ibox_get_text(struct input_box *box); | |||
void widget_ibox_destroy(struct input_box *box); | |||
void widget_ibox_set_error(struct input_box *box, char *err); | |||
void widget_ibox_set_msg(struct input_box *box, char *msg); |
@@ -0,0 +1,513 @@ | |||
/* | |||
* neercs console-based window manager | |||
* Copyright (c) 2006-2010 Sam Hocevar <sam@hocevar.net> | |||
* 2008-2010 Jean-Yves Lamoureux <jylam@lnxscene.org> | |||
* All Rights Reserved | |||
* | |||
* This program is free software. It comes without any warranty, to | |||
* the extent permitted by applicable law. You can redistribute it | |||
* and/or modify it under the terms of the Do What The Fuck You Want | |||
* To Public License, Version 2, as published by Sam Hocevar. See | |||
* http://sam.zoy.org/wtfpl/COPYING for more details. | |||
*/ | |||
#include "config.h" | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <math.h> | |||
#include <caca.h> | |||
#include "neercs.h" | |||
void resize_screen(struct screen *s, int w, int h) | |||
{ | |||
caca_canvas_t *old, *new; | |||
if (w == s->w && h == s->h) | |||
return; | |||
if (w <= 0 || h <= 0) | |||
return; | |||
s->changed = 1; | |||
s->w = w; | |||
s->h = h; | |||
/* | |||
* caca_set_canvas_boundaries() is bugged as hell, so let's resize it by | |||
* hands | |||
*/ | |||
old = s->cv; | |||
new = caca_create_canvas(w, h); | |||
caca_blit(new, 0, 0, old, NULL); | |||
s->cv = new; | |||
caca_gotoxy(new, caca_get_cursor_x(old), caca_get_cursor_y(old)); | |||
caca_free_canvas(old); | |||
set_tty_size(s->fd, w, h); | |||
s->orig_w = s->w; | |||
s->orig_h = s->h; | |||
s->orig_x = s->x; | |||
s->orig_y = s->y; | |||
} | |||
void update_windows_props(struct screen_list *screen_list) | |||
{ | |||
debug("%s, %d screens, type %d\n", __FUNCTION__, screen_list->count, | |||
screen_list->wm_type); | |||
if (!screen_list->count) | |||
return; | |||
switch (screen_list->wm_type) | |||
{ | |||
case WM_CARD: | |||
update_windows_props_cards(screen_list); | |||
break; | |||
case WM_HSPLIT: | |||
update_windows_props_hsplit(screen_list); | |||
break; | |||
case WM_VSPLIT: | |||
update_windows_props_vsplit(screen_list); | |||
break; | |||
case WM_FULL: | |||
default: | |||
update_windows_props_full(screen_list); | |||
break; | |||
} | |||
} | |||
void update_windows_props_hsplit(struct screen_list *screen_list) | |||
{ | |||
int i; | |||
int w = | |||
(screen_list->width / screen_list->count) - | |||
(screen_list->border_size * 2); | |||
int h = screen_list->height - (screen_list->border_size * 2); | |||
for (i = 0; i < screen_list->count; i++) | |||
{ | |||
screen_list->screen[i]->x = (i * w) + screen_list->border_size; | |||
screen_list->screen[i]->y = screen_list->border_size; | |||
screen_list->screen[i]->visible = 1; | |||
if (i != screen_list->count - 1) | |||
{ | |||
resize_screen(screen_list->screen[i], w - 1, h); | |||
} | |||
else | |||
{ | |||
resize_screen(screen_list->screen[i], | |||
screen_list->width - i * w - 2, h); | |||
} | |||
} | |||
} | |||
void update_windows_props_vsplit(struct screen_list *screen_list) | |||
{ | |||
int i; | |||
int w = screen_list->width - (screen_list->border_size * 2); | |||
int h = (screen_list->height) / screen_list->count; | |||
for (i = 0; i < screen_list->count; i++) | |||
{ | |||
screen_list->screen[i]->x = screen_list->border_size; | |||
screen_list->screen[i]->y = (i * h) + (screen_list->border_size); | |||
screen_list->screen[i]->visible = 1; | |||
if (i != screen_list->count - 1) | |||
{ | |||
resize_screen(screen_list->screen[i], w, | |||
h - (screen_list->border_size * 2)); | |||
} | |||
else | |||
{ | |||
resize_screen(screen_list->screen[i], | |||
w, | |||
screen_list->height - i * h - | |||
(screen_list->border_size * 2)); | |||
} | |||
} | |||
} | |||
void update_windows_props_full(struct screen_list *screen_list) | |||
{ | |||
int i; | |||
int w = screen_list->width - (screen_list->border_size * 2); | |||
int h = screen_list->height - (screen_list->border_size * 2); | |||
for (i = 0; i < screen_list->count; i++) | |||
{ | |||
screen_list->screen[i]->visible = 0; | |||
screen_list->screen[i]->x = screen_list->border_size; | |||
screen_list->screen[i]->y = screen_list->border_size; | |||
resize_screen(screen_list->screen[i], w, h); | |||
} | |||
screen_list->screen[screen_list->pty]->visible = 1; | |||
} | |||
void update_windows_props_cards(struct screen_list *screen_list) | |||
{ | |||
int i; | |||
int w = (screen_list->width - screen_list->count * 3) + 1; | |||
int h = (screen_list->height - screen_list->count) - 1; | |||
int x = 1; | |||
int y = screen_list->count; | |||
for (i = 0; i < screen_list->count; i++) | |||
{ | |||
screen_list->screen[i]->visible = 1; | |||
screen_list->screen[i]->x = x; | |||
screen_list->screen[i]->y = y; | |||
resize_screen(screen_list->screen[i], w, h); | |||
x += 3; | |||
y--; | |||
} | |||
} | |||
/* Window managers refresh */ | |||
void wm_refresh(struct screen_list *screen_list) | |||
{ | |||
/* FIXME : move set_color to a relevant place */ | |||
caca_set_color_ansi(screen_list->cv, CACA_LIGHTRED, CACA_BLACK); | |||
switch (screen_list->wm_type) | |||
{ | |||
case WM_CARD: | |||
wm_refresh_card(screen_list); | |||
break; | |||
case WM_HSPLIT: | |||
wm_refresh_hsplit(screen_list); | |||
break; | |||
case WM_VSPLIT: | |||
wm_refresh_hsplit(screen_list); | |||
break; | |||
case WM_FULL: | |||
default: | |||
wm_refresh_cube(screen_list); | |||
break; | |||
} | |||
} | |||
static void wm_bell(struct screen_list *screen_list) | |||
{ | |||
if (screen_list->screen[screen_list->pty]->bell) | |||
{ | |||
caca_set_color_ansi(screen_list->cv, CACA_RED, CACA_BLACK); | |||
screen_list->in_bell--; | |||
screen_list->force_refresh = 1; | |||
if (!screen_list->in_bell) | |||
{ | |||
screen_list->was_in_bell = 1; | |||
screen_list->screen[screen_list->pty]->bell = 0; | |||
} | |||
} | |||
else | |||
{ | |||
if (screen_list->was_in_bell) | |||
{ | |||
screen_list->screen[screen_list->pty]->bell = 0; | |||
screen_list->force_refresh = 1; | |||
screen_list->was_in_bell = 0; | |||
screen_list->changed = 1; | |||
} | |||
caca_set_color_ansi(screen_list->cv, CACA_LIGHTGREEN, CACA_BLACK); | |||
} | |||
} | |||
static void wm_box(struct screen_list *screen_list, int pty) | |||
{ | |||
if (!screen_list->screen[pty]->changed && !screen_list->changed) | |||
return; | |||
if (!screen_list->border_size) | |||
return; | |||
/* Color determined by wm_bell() */ | |||
caca_draw_cp437_box(screen_list->cv, | |||
screen_list->screen[pty]->x - 1, | |||
screen_list->screen[pty]->y - 1, | |||
screen_list->screen[pty]->w + 2, | |||
screen_list->screen[pty]->h + 2); | |||
if (screen_list->screen[pty]->title) | |||
{ | |||
caca_printf(screen_list->cv, | |||
screen_list->screen[pty]->x, | |||
screen_list->screen[pty]->y - 1, | |||
" %.*s ", | |||
screen_list->screen[pty]->w - 3, | |||
screen_list->screen[pty]->title); | |||
} | |||
} | |||
static void wm_blit_current_screen(struct screen_list *screen_list) | |||
{ | |||
if (screen_list->screen[screen_list->pty]->changed || screen_list->changed) | |||
caca_blit(screen_list->cv, | |||
screen_list->screen[screen_list->pty]->x, | |||
screen_list->screen[screen_list->pty]->y, | |||
screen_list->screen[screen_list->pty]->cv, NULL); | |||
} | |||
void wm_refresh_card(struct screen_list *screen_list) | |||
{ | |||
int i; | |||
for (i = screen_list->count - 1; i >= 0; i--) | |||
{ | |||
if (i != screen_list->pty && screen_list->screen[i]->visible && | |||
(screen_list->screen[i]->changed || screen_list->changed)) | |||
{ | |||
caca_blit(screen_list->cv, | |||
screen_list->screen[i]->x, | |||
screen_list->screen[i]->y, | |||
screen_list->screen[i]->cv, NULL); | |||
wm_box(screen_list, i); | |||
} | |||
} | |||
/* Force 'changed' to force redraw */ | |||
screen_list->screen[screen_list->pty]->changed = 1; | |||
wm_blit_current_screen(screen_list); | |||
wm_bell(screen_list); | |||
wm_box(screen_list, screen_list->pty); | |||
} | |||
void wm_refresh_full(struct screen_list *screen_list) | |||
{ | |||
wm_blit_current_screen(screen_list); | |||
wm_bell(screen_list); | |||
wm_box(screen_list, screen_list->pty); | |||
} | |||
void wm_refresh_vsplit(struct screen_list *screen_list) | |||
{ | |||
int i; | |||
for (i = screen_list->count - 1; i >= 0; i--) | |||
{ | |||
if (i != screen_list->pty && screen_list->screen[i]->visible && | |||
(screen_list->screen[i]->changed || screen_list->changed)) | |||
{ | |||
caca_blit(screen_list->cv, | |||
screen_list->screen[i]->x, | |||
screen_list->screen[i]->y, | |||
screen_list->screen[i]->cv, NULL); | |||
wm_box(screen_list, i); | |||
} | |||
} | |||
wm_blit_current_screen(screen_list); | |||
wm_bell(screen_list); | |||
wm_box(screen_list, screen_list->pty); | |||
} | |||
void wm_refresh_hsplit(struct screen_list *screen_list) | |||
{ | |||
int i; | |||
for (i = screen_list->count - 1; i >= 0; i--) | |||
{ | |||
if (i != screen_list->pty && screen_list->screen[i]->visible && | |||
(screen_list->screen[i]->changed || screen_list->changed)) | |||
{ | |||
caca_blit(screen_list->cv, | |||
screen_list->screen[i]->x, | |||
screen_list->screen[i]->y, | |||
screen_list->screen[i]->cv, NULL); | |||
wm_box(screen_list, i); | |||
} | |||
} | |||
wm_blit_current_screen(screen_list); | |||
wm_bell(screen_list); | |||
wm_box(screen_list, screen_list->pty); | |||
} | |||
static float | |||
get_direction(float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) | |||
{ | |||
float d1x, d1y, d2x, d2y; | |||
d1x = p3x - p1x; | |||
d1y = p3y - p1y; | |||
d2x = p3x - p2x; | |||
d2y = p3y - p2y; | |||
return (d1x * d2y) - (d1y * d2x); | |||
} | |||
/* 3D Cube. Yeah I know, it's a mess. Just look anywhere else. */ | |||
static void draw_face(caca_canvas_t * cv, | |||
int p1x, int p1y, | |||
int p2x, int p2y, | |||
int p3x, int p3y, | |||
int p4x, int p4y, caca_canvas_t * tex, | |||
int color, int borders) | |||
{ | |||
if (get_direction(p1x, p1y, p2x, p2y, p3x, p3y) >= 0) | |||
{ | |||
int coords[6]; | |||
float uv[6]; | |||
coords[0] = p1x; | |||
coords[1] = p1y; | |||
coords[2] = p2x; | |||
coords[3] = p2y; | |||
coords[4] = p3x; | |||
coords[5] = p3y; | |||
uv[0] = 1; | |||
uv[1] = 1; | |||
uv[2] = 0; | |||
uv[3] = 1; | |||
uv[4] = 0; | |||
uv[5] = 0; | |||
caca_fill_triangle_textured(cv, coords, tex, uv); | |||
coords[0] = p1x; | |||
coords[1] = p1y; | |||
coords[2] = p3x; | |||
coords[3] = p3y; | |||
coords[4] = p4x; | |||
coords[5] = p4y; | |||
uv[0] = 1; | |||
uv[1] = 1; | |||
uv[2] = 0; | |||
uv[3] = 0; | |||
uv[4] = 1; | |||
uv[5] = 0; | |||
caca_fill_triangle_textured(cv, coords, tex, uv); | |||
caca_set_color_ansi(cv, color, CACA_BLACK); | |||
if (borders) | |||
{ | |||
caca_draw_thin_line(cv, p1x, p1y, p2x, p2y); | |||
caca_draw_thin_line(cv, p2x, p2y, p3x, p3y); | |||
caca_draw_thin_line(cv, p3x, p3y, p4x, p4y); | |||
caca_draw_thin_line(cv, p4x, p4y, p1x, p1y); | |||
} | |||
} | |||
} | |||
void wm_refresh_cube(struct screen_list *screen_list) | |||
{ | |||
int i; | |||
if (!screen_list->cube.in_switch || !screen_list->eyecandy) | |||
{ | |||
wm_refresh_full(screen_list); | |||
// screen_list->force_refresh = 0; | |||
} | |||
else | |||
{ | |||
long long unsigned int cur_time = get_us() - screen_list->last_switch; | |||
if (cur_time >= screen_list->cube.duration || screen_list->count == 1) | |||
{ | |||
screen_list->changed = 1; | |||
screen_list->force_refresh = 1; | |||
screen_list->cube.in_switch = 0; | |||
} | |||
else | |||
{ | |||
float cube[12][3] = { | |||
{-1, -1, 1}, | |||
{1, -1, 1}, | |||
{1, 1, 1}, | |||
{-1, 1, 1}, | |||
{1, -1, 1}, | |||
{1, -1, -1}, | |||
{1, 1, -1}, | |||
{1, 1, 1}, | |||
{-1, -1, -1}, | |||
{-1, -1, 1}, | |||
{-1, 1, 1}, | |||
{-1, 1, -1}, | |||
}; | |||
float cube_transformed[12][3]; | |||
float cube_projected[12][2]; | |||
float fov = 0.5f; | |||
float angle = | |||
90.0f * ((float)cur_time / (float)screen_list->cube.duration); | |||
angle *= (M_PI / 180.0f); | |||
if (screen_list->cube.side == 1) | |||
angle = -angle; | |||
float sina = sin(angle); | |||
float cosa = cos(angle); | |||
for (i = 0; i < 12; i++) | |||
{ | |||
cube_transformed[i][2] = cube[i][2] * cosa - cube[i][0] * sina; | |||
cube_transformed[i][0] = cube[i][2] * sina + cube[i][0] * cosa; | |||
cube_transformed[i][1] = cube[i][1]; | |||
cube_transformed[i][2] -= 3; | |||
cube_projected[i][0] = | |||
cube_transformed[i][0] / (cube_transformed[i][2] * fov); | |||
cube_projected[i][1] = | |||
cube_transformed[i][1] / (cube_transformed[i][2] * fov); | |||
cube_projected[i][0] /= 2.0f; | |||
cube_projected[i][1] /= 2.0f; | |||
cube_projected[i][0] += 0.5f; | |||
cube_projected[i][1] += 0.5f; | |||
cube_projected[i][0] *= screen_list->width; | |||
cube_projected[i][1] *= screen_list->height; | |||
} | |||
caca_set_color_ansi(screen_list->cv, CACA_WHITE, CACA_BLACK); | |||
caca_clear_canvas(screen_list->cv); | |||
caca_canvas_t *first = | |||
screen_list->screen[screen_list->prevpty]->cv; | |||
caca_canvas_t *second = screen_list->screen[screen_list->pty]->cv; | |||
draw_face(screen_list->cv, | |||
cube_projected[0][0], cube_projected[0][1], | |||
cube_projected[1][0], cube_projected[1][1], | |||
cube_projected[2][0], cube_projected[2][1], | |||
cube_projected[3][0], cube_projected[3][1], | |||
first, CACA_LIGHTGREEN, screen_list->border_size); | |||
if (screen_list->cube.side) | |||
{ | |||
draw_face(screen_list->cv, | |||
cube_projected[4][0], cube_projected[4][1], | |||
cube_projected[5][0], cube_projected[5][1], | |||
cube_projected[6][0], cube_projected[6][1], | |||
cube_projected[7][0], cube_projected[7][1], | |||
second, CACA_LIGHTGREEN, screen_list->border_size); | |||
} | |||
else | |||
{ | |||
draw_face(screen_list->cv, | |||
cube_projected[8][0], cube_projected[8][1], | |||
cube_projected[9][0], cube_projected[9][1], | |||
cube_projected[10][0], cube_projected[10][1], | |||
cube_projected[11][0], cube_projected[11][1], | |||
second, CACA_LIGHTGREEN, screen_list->border_size); | |||
} | |||
screen_list->changed = 1; | |||
screen_list->force_refresh = 1; | |||
screen_list->cube.in_switch = 1; | |||
} | |||
} | |||
} |