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