/* * neercs console-based window manager * Copyright (c) 2006 Sam Hocevar * 2008 Jean-Yves Lamoureux * 2008 Pascal Terjan * All Rights Reserved * * $Id$ * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "neercs.h" static int send_to_client(const char * msg, struct screen_list* screen_list) { int ret; if(!screen_list->socket[SOCK_CLIENT]) connect_socket(screen_list, SOCK_CLIENT); debug("Sending message (%s) to client on socket %d", msg, screen_list->socket[SOCK_CLIENT]); if(!screen_list->socket[SOCK_CLIENT]) ret = -1; else ret = write(screen_list->socket[SOCK_CLIENT], msg, strlen(msg)); if(ret < 0 && errno != EAGAIN) { fprintf(stderr, "Failed to send message to client: %s\n", strerror(errno)); detach(screen_list); } return ret; } static int set_title(const char * title, struct screen_list* screen_list) { char buf[1024]; int bytes; bytes = snprintf(buf, sizeof(buf)-1, "TITLE %s", title); buf[bytes] = '\0'; return send_to_client(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, screen_list); } static int request_refresh(struct screen_list* screen_list) { size_t bytes; void *buf; char *buf2; buf = cucul_export_memory (screen_list->cv, "caca", &bytes); buf2 = malloc(bytes+8); memcpy(buf2, "REFRESH ", 8); memcpy(buf2+8, buf, bytes); if(!screen_list->socket[SOCK_CLIENT]) connect_socket(screen_list, SOCK_CLIENT); if(screen_list->socket[SOCK_CLIENT]) if(write(screen_list->socket[SOCK_CLIENT], buf2, bytes+8) <= 0 && errno != EAGAIN) { return -1; } free(buf); free(buf2); return 0; } int detach(struct screen_list* screen_list) { screen_list->attached = 0; if(screen_list->socket[SOCK_CLIENT]) { send_to_client("DETACH", screen_list); close(screen_list->socket[SOCK_CLIENT]); screen_list->socket[SOCK_CLIENT] = 0; } return 0; } static int server_main(int *to_grab, char **to_start, struct screen_list *screen_list) { int i; int eof = 0, refresh = 1, command = 0; long long unsigned int last_key_time = 0; int mainret = 0; screen_list->attached = 0; /* 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 = 10; /* Create main canvas */ screen_list->cv = cucul_create_canvas(screen_list->width, screen_list->height + screen_list->mini*6 + screen_list->status); if(!to_grab && !to_start) { add_screen(screen_list, create_screen(screen_list->width, screen_list->height, screen_list->default_shell)); } /* Attach processes */ if(to_grab) { for(i=0; to_grab[i]; i++) { add_screen(screen_list, create_screen_grab(screen_list->width, screen_list->height, to_grab[i])); } free(to_grab); } /* Launch command line processes */ if(to_start) { for(i=0; to_start[i]; i++) { add_screen(screen_list, create_screen(screen_list->width, screen_list->height, to_start[i])); } free(to_start); } last_key_time = get_us(); for(;;) { int quit = 0; ssize_t n; char buf[4097]; /* Read program output */ refresh |= update_screens_contents(screen_list); /* Check if we got something from the client */ while (screen_list->socket[SOCK_SERVER] && (n = read(screen_list->socket[SOCK_SERVER], buf, sizeof(buf)-1)) > 0) { buf[n] = 0; debug("Received command %s", buf); if(!strncmp("ATTACH ", buf, 7)) { screen_list->attached = 1; cucul_free_canvas(screen_list->cv); screen_list->cv = cucul_create_canvas(atoi(buf+7), atoi(buf+18)); screen_list->width = cucul_get_canvas_width(screen_list->cv); screen_list->height = cucul_get_canvas_height(screen_list->cv) - ((screen_list->mini*6) + (screen_list->status)); update_windows_props(screen_list); cucul_clear_canvas(screen_list->cv); refresh = 1; } else if(!strncmp("QUIT", buf, 4)) { quit = 1; } else if(!strncmp("RESIZE ", buf, 7)) { cucul_free_canvas(screen_list->cv); screen_list->cv = cucul_create_canvas(atoi(buf+7), atoi(buf+18)); screen_list->width = cucul_get_canvas_width(screen_list->cv); screen_list->height = cucul_get_canvas_height(screen_list->cv) - ((screen_list->mini*6) + (screen_list->status)); update_windows_props(screen_list); cucul_clear_canvas(screen_list->cv); refresh = 1; } else if(!strncmp("KEY ", buf, 4)) { unsigned int c = atoi(buf+4); char *str = NULL; int size = 0; /* CTRL-A has been pressed before, handle this as a * command, except that CTRL-A a sends literal CTRL-A */ if(command && (c != 'a')) { command = 0; refresh |= handle_command_input(screen_list, c); } else { /* Not in command mode */ last_key_time = get_us(); set_cursor(1, screen_list); /* Kill screensaver */ if(screen_list->in_screensaver) { screensaver_kill(screen_list); screen_list->in_screensaver = 0; refresh = 1; continue; } /* Handle lock window */ if(screen_list->locked) refresh |= update_lock(c, screen_list); else { switch(c) { case 0x01: //CACA_KEY_CTRL_A: command = 1; break; case CACA_KEY_ESCAPE: if(screen_list->help) { screen_list->help = 0; refresh = 1; break; } default: /* CTRL-A a sends literal CTRL-A */ if (command && (c == 'a')) { c = 0x01; } /* Normal key, convert it if needed */ str = convert_input_ansi(&c, &size); write(screen_list->screen[screen_list->pty]->fd, str, size); break; } } } } else { fprintf(stderr, "Unknown command received: %s\n", buf); } } /* No more screens, exit */ if(!screen_list->count) break; /* User requested to exit */ if(quit) break; /* Update each screen canvas */ refresh |= update_terms(screen_list); /* Launch reccurents if any */ refresh |= handle_recurrents(screen_list); /* Resfresh screen */ if(!screen_list->attached) { /* No need to refresh * Don't use the CPU too much * Would be better to select on terms + socket */ sleep(1); } /* Draw lock window */ else if(screen_list->locked) { draw_lock(screen_list); refresh = 1; } else { if((refresh || screen_list->in_bell) && (get_us() - last_key_time < screen_list->screensaver_timeout)) { refresh_screens(screen_list); if(screen_list->attached) { if(screen_list->pty < screen_list->count && screen_list->screen[screen_list->pty]->title) set_title(screen_list->screen[screen_list->pty]->title, screen_list); else set_title(PACKAGE_STRING, screen_list); } refresh = 1; } if((get_us() - last_key_time > screen_list->screensaver_timeout)) { if(!screen_list->in_screensaver) { screensaver_init(screen_list); screen_list->in_screensaver = 1; set_cursor(0, screen_list); } draw_screensaver(screen_list); refresh = 1; } if((get_us() - last_key_time > screen_list->autolock_timeout)) { screen_list->locked = 1; refresh = 1; } } if(refresh) { if(screen_list->attached) request_refresh(screen_list); refresh = 0; } eof = 1; for(i=0; i < screen_list->count; i++) if(screen_list->screen[i]->fd >= 0) eof = 0; if(eof) break; } detach(screen_list); /* Clean up */ cucul_free_canvas(screen_list->cv); for(i = 0; i < screen_list->count; i++) { destroy_screen(screen_list->screen[i]); } if(screen_list->socket_path[SOCK_SERVER]) { unlink(screen_list->socket_path[SOCK_SERVER]); free(screen_list->socket_path[SOCK_SERVER]); } if(screen_list->socket_path[SOCK_CLIENT]) free(screen_list->socket_path[SOCK_CLIENT]); if(screen_list->socket[SOCK_SERVER]) close(screen_list->socket[SOCK_SERVER]); if(screen_list->socket[SOCK_CLIENT]) close(screen_list->socket[SOCK_CLIENT]); if(screen_list->screen) free(screen_list->screen); for(i=0; irecurrent_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->session_name) free(screen_list->session_name); if(screen_list) free(screen_list); return mainret; } int start_server(int *to_grab, char **to_start, struct screen_list *screen_list) { pid_t pid; char * sess; pid = fork(); if(pid < 0) { fprintf(stderr, "Failed to create child process\n"); return 1; } if(pid == 0) { int fd; close(0); close(1); close(2); fd = open("/dev/null", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR); dup2(fd, 0); #ifndef DEBUG dup2(fd, 1); dup2(fd, 2); if (fd > 2) close(fd); #else if(fd != 0) close(fd); fd = open("/tmp/log.txt", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR); dup2(fd, 1); dup2(fd, 2); if (fd > 2) close(fd); #endif setsid(); return server_main(to_grab, to_start, screen_list); } 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 = cucul_create_canvas(0, 0); screen_list->dp = caca_create_display(screen_list->cv); if(!screen_list->dp) return 1; caca_set_cursor(screen_list->dp, 1); request_attach(screen_list); return 0; } long long get_us(void) { struct timeval tv; gettimeofday(&tv, NULL); return (tv.tv_sec*(1000000) + tv.tv_usec); }