@@ -6,13 +6,15 @@ endif | |||||
neercs_SOURCES = \ | neercs_SOURCES = \ | ||||
neercs.cpp neercs.h \ | neercs.cpp neercs.h \ | ||||
\ | \ | ||||
$(old_sources) \ | |||||
\ | |||||
video/render.cpp video/render.h \ | video/render.cpp video/render.h \ | ||||
video/text-render.cpp video/text-render.h \ | video/text-render.cpp video/text-render.h \ | ||||
video/simple.lolfx \ | video/simple.lolfx \ | ||||
video/blurh.lolfx video/blurv.lolfx \ | video/blurh.lolfx video/blurv.lolfx \ | ||||
video/remanency.lolfx video/glow.lolfx video/postfx.lolfx video/radial.lolfx \ | video/remanency.lolfx video/glow.lolfx video/postfx.lolfx video/radial.lolfx \ | ||||
video/text.lolfx | video/text.lolfx | ||||
neercs_CPPFLAGS = @LOL_CFLAGS@ @PIPI_CFLAGS@ @CACA_CFLAGS@ | |||||
neercs_CPPFLAGS = @LOL_CFLAGS@ @PIPI_CFLAGS@ @CACA_CFLAGS@ -Iold | |||||
neercs_LDADD = | neercs_LDADD = | ||||
neercs_LDFLAGS = $(top_builddir)/src/liblol.a \ | neercs_LDFLAGS = $(top_builddir)/src/liblol.a \ | ||||
@LOL_LIBS@ @PIPI_LIBS@ @CACA_LIBS@ @UTIL_LIBS@ @PAM_LIBS@ | @LOL_LIBS@ @PIPI_LIBS@ @CACA_LIBS@ @UTIL_LIBS@ @PAM_LIBS@ | ||||
@@ -31,3 +33,32 @@ SUFFIXES = .lolfx | |||||
.lolfx.o: | .lolfx.o: | ||||
$(LOLFX_BUILD) | $(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 "neercs.h" | ||||
#include "video/render.h" | #include "video/render.h" | ||||
Neercs::Neercs() | |||||
extern "C" | |||||
{ | |||||
#include "old/neercs.h" | |||||
} | |||||
Neercs::Neercs(int argc, char **argv) | |||||
: m_ready(false), | : m_ready(false), | ||||
m_caca(caca_create_canvas(10, 10)), | m_caca(caca_create_canvas(10, 10)), | ||||
m_render(new Render(m_caca)), | m_render(new Render(m_caca)), | ||||
@@ -140,7 +145,7 @@ int main(int argc, char **argv) | |||||
_chdir("../.."); | _chdir("../.."); | ||||
#endif | #endif | ||||
new Neercs(); | |||||
new Neercs(argc, argv); | |||||
new DebugFps(2, 2); | new DebugFps(2, 2); | ||||
app.ShowPointer(false); | app.ShowPointer(false); | ||||
@@ -14,7 +14,7 @@ | |||||
class Neercs : public WorldEntity | class Neercs : public WorldEntity | ||||
{ | { | ||||
public: | public: | ||||
Neercs(); | |||||
Neercs(int argc, char **argv); | |||||
virtual ~Neercs(); | virtual ~Neercs(); | ||||
char const *GetName() { return "<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; | |||||
} | |||||
} | |||||
} |