/* * windowwatch.c * * Monitoriza un display para eventos de nueva ventana (o de destrucción * de ventana), e informa al tciiserver de dichos eventos. * * La información de cómo hacer esta monitorización ha sido obtenida de * xtoolwait, de Richad Huveneers (GPL 2). * * Historia: * 1/10/04 Creación. Ya dispone de la funcionalidad esperada :-). * 16/11/04 Corrijo dos bugs por los que no ponía el socket a -1 * después de cerrarlo (y entonces no intentaba volver a * conectarse). * 23/11/04 Hago que ponga la pantalla supervisada en el mensaje de id. * Cambio el formato de los mensajes a "zoom map" y "zoom unmap" * 3/12/04 Hago que use por defecto el puerto de autozoom de tcii. * 28/02/05 Comento lo de que ignore las ventanas con override-redirect * (ya que etsba ignorando TODAS las ventanas :-?). * * Autor: Dario Rodriguez dario@softhome.net * (c) 2004 SICO software. */ /* Fihceros de cabecera */ #include #include #include #include #include #include #include #include "simplesocket.h" #include "simplestring.h" /* Constantes */ #define TAMBUFRECEPCION 1024 #define DEFAULT_PORT "8102" /* Macros */ #ifdef DEBUG #undef DEBUG #define DEBUG(x) debug_print x static int debug_print(char *cadena, ...) { va_list lista;fprintf(stderr,"DEBUG: ");va_start(lista,cadena); vfprintf(stderr,cadena,lista);va_end(lista);fflush(stderr); return(0); } static char *ev2str(int ev) { char *cads[]={"Reserved","Reserved","KeyPress","KeyRelease", "ButtonPress","ButtonRelease","MotionNotify","EnterNotify", "LeaveNotify","FocusIn","FocusOut","KeymapNotify","Expose", "GraphicsExpose","NoExpose","VisibilityNotify","CreateNotify", "DestroyNotify","UnmapNotify","MapNotify","MapRequest", "ReparentNotify","ConfigureNotify","ConfigureRequest", "GravityNotify","ResizeRequest","CirculateNotify", "CirculateRequest","PropertyNotify","SelectionClear", "SelectionRequest","SelectionNotify","ColormapNotify", "ClientMessage","MappingNotify","LASTEvent"}; if(ev<0 || ev>sizeof(cads)/sizeof(cads[0])) return("Unknown"); return(cads[ev]); } #else #define DEBUG(x) #endif /* Tipos de datos */ typedef struct TipoConexion { char *host; long tam_host; int puerto; int enchufe; char *nombre_display; sBuffer *buf; } sConexion; /* Variables globales */ int (*default_x_error_func)(Display *dpy, XErrorEvent *err); volatile int bandera_sigint=0; /* Prototipos de funciones auxiliares */ static int error_fatal(char *cadena, ...); static int x_error_func(Display *dpy, XErrorEvent *err); static int instala_senial(int Num,void (*f)(int)); static void sigint(int Num); /* Prototipos de las funciones que gestionan la conexion */ sConexion *con_init(char *host, char *puerto, char *nombre_display); void con_libera(sConexion *con); int con_conexion_ok(sConexion *con); int con_conecta(sConexion *con); int con_escribe(sConexion *con,char *formato,...); char *con_recibe(sConexion *con); /* Punto de entrada al programa */ int main(int argc, char *argv[]) { char *nombre_display,*host_tcii,*puerto_tcii; Display *dpy; Atom xa_wm_state; XEvent event; sConexion *conexion; char *msg; /* Parseamos los parámetros */ if(argc!=3 && argc!=4) return(error_fatal("Número de parámetros no válido" "\nSintaxis:\n\t%s nombredisplay" " hosttcii [puertotcii]\n",argv[0])); nombre_display=argv[1],host_tcii=argv[2]; puerto_tcii=(argc==4)?argv[3]:DEFAULT_PORT; /* Inicializamos los datos de conexión */ if((conexion=con_init(host_tcii,puerto_tcii,nombre_display))==NULL) return(error_fatal("No se conoce el host/puerto %s:%s", host_tcii,puerto_tcii)); /* Abrimos el display */ if((dpy=XOpenDisplay(nombre_display))==NULL) return(error_fatal("No se pudo abrir el display %s", nombre_display)); /* Ponemos un manejador de errores de X */ default_x_error_func=XSetErrorHandler(x_error_func); /* Seleccionamos recibir eventos de la ventana raíz */ XSelectInput(dpy,DefaultRootWindow(dpy),SubstructureNotifyMask); /* Obtenemos el Atom de la propiedad WM_STATE */ xa_wm_state = XInternAtom(dpy, "WM_STATE", False); if(xa_wm_state==None) { XCloseDisplay(dpy); return(error_fatal("WM_STATE no encontrado\n" "Compruebe que el gestor de ventanas " "está arrancado.\n")); } /* Ponemos un manejador de Control+C */ instala_senial(SIGINT,sigint); /* Bucle principal esperando mapeos */ while(!bandera_sigint) { /* Si no estamos conectados, lo hacemos ahora */ if(con_conexion_ok(conexion)!=0) con_conecta(conexion); /* Procesamos los mensajes de "arriba" */ while((msg=con_recibe(conexion))!=NULL) { /* Por ahora ignoramos estos mensajes */ ; } /* NOTA: como XNextEvent() es bloqueante, no nos enteraremos */ /* de un sigint hasta que ocurra un evento después de la señal*/ #warning En estos momentos si se pulsa Control+C espera un evento mas antes de salir XNextEvent(dpy, &event); if(event.type==CreateNotify) { DEBUG(("Yipes! Evento de creación: %s(%d) %d\n", ev2str((int)event.type), (int)event.type,(int)event.xany.window)); if(event.xcreatewindow.send_event || event.xcreatewindow.override_redirect) { DEBUG(("Era fake o pop-up (ignorándolo)\n")); continue; } /* Nos añadimos a la lista de clientes interesados */ /* en los eventos de esa ventana */ XSelectInput(dpy,event.xcreatewindow.window, PropertyChangeMask); } else if(event.type==MapNotify) { Window raiz; int x,y; unsigned int width,height,border,depth; DEBUG(("Yipes! Evento de mapping: %s(%d) %d\n", ev2str((int)event.type), (int)event.type,(int)event.xany.window)); #if 0 /* Si es una ventana con override-redirect, la */ /* ignoramos */ if(event.xmap.override_redirect) { DEBUG(("Mapping de un pop-up (ignorándolo)\n")); continue; } #endif /* Intentamos obtener "más" información */ XGetGeometry(dpy,event.xmap.window,&raiz,&x,&y,&width, &height,&border,&depth); DEBUG(("x=%li y=%li width=%li height=%li border=%li" " depth=%li\n",(long)x,(long)y,(long)width, (long)height,(long)border,(long)depth)); /* Lo mandamos por la línea */ con_escribe(conexion,"zoom map x=%li y=%li width=%li " "height=%li\n", (long)x,(long)y, (long)width,(long)height); } else if(event.type==UnmapNotify) { DEBUG(("Yipes! Evento de unmapping: %s(%d) %d\n", ev2str((int)event.type), (int)event.type,(int)event.xany.window)); con_escribe(conexion,"zoom unmap\n"); } else { DEBUG(("Ignorando evento: %s(%d) %d\n", ev2str((int)event.type), (int)event.type,(int)event.xany.window)); } } /* Hemos terminado */ XCloseDisplay(dpy); con_libera(conexion); return(0); } /* Cuerpo de las funciones auxiliares */ static int error_fatal(char *cadena, ...) { va_list lista; fprintf(stderr,"ERROR: "); va_start(lista,cadena); vfprintf(stderr,cadena,lista); va_end(lista); fflush(stderr); return(1); } static int x_error_func(Display *dpy, XErrorEvent *err) { fprintf(stderr,"AVISO: X error: cod %d, major %d\n", (int)err->error_code,(int)err->request_code); if(err->error_code==BadWindow) { /* Ignoramos los errores de intentar acceder a ventanas que */ /* no existen ya */ fprintf(stderr,"AVISO: X error: ignorando BadWindow\n"); return(0); } else if(err->error_code==BadDrawable) { /* Ignoramos los errores de intentar acceder a ventanas que */ /* no existen ya */ fprintf(stderr,"AVISO: X error: ignorando BadDrawable\n"); return(0); } return(default_x_error_func(dpy,err)); } static int instala_senial(int Num,void (*f)(int)) { struct sigaction sact; sact.sa_handler=f; sigemptyset(&sact.sa_mask); sact.sa_flags=0; return(sigaction(Num,&sact,0)); } static void sigint(int Num) { bandera_sigint=1; } /* Gestión de la conexión con tciiserver */ sConexion * con_init(char *host, char *puerto, char *nombre_display) { sConexion *con; /* Obtenemos memoria para la estructura */ if((con=malloc(sizeof(sConexion)))==NULL) return(NULL); memset(con,0,sizeof(sConexion)); /* Obtenemos la ip y el puerto */ if((con->host=SockGenIp(host,&(con->tam_host)))==NULL) { free(con); return(NULL); } if((con->puerto=SockGenPort(puerto,-1))==-1) { free(con->host),free(con); return(NULL); } if((con->nombre_display=strdup(nombre_display))==NULL) { free(con->host),free(con); return(NULL); } if((con->buf=SBufferInit(TAMBUFRECEPCION))==NULL) { free(con->nombre_display),free(con->host),free(con); return(NULL); } /* Inicializamos el file descriptor a "no conectado" */ con->enchufe=-1; /* salimos con éxito */ return(con); } void con_libera(sConexion *con) { if(con==NULL) return; if(con->enchufe!=-1) close(con->enchufe),con->enchufe=-1; if(con->host!=NULL) free(con->host),con->host=NULL,con->tam_host=0; if(con->nombre_display!=NULL) free(con->nombre_display),con->nombre_display=NULL; if(con->buf!=NULL) SBufferLibera(con->buf),con->buf=NULL; free(con); } int con_conexion_ok(sConexion *con) { int res; /* Comprobaciones de sanidad */ if(con==NULL) return(-1); if(con->enchufe==-1) return(-1); /* Hacemos la comprobación de si hay datos a recibir y queued es 0 */ if(SockSelect(0,004,con->enchufe,&res,-1)>=0) { if(SockQueued(con->enchufe)<=0) { /* Socket roto */ close(con->enchufe),con->enchufe=-1; return(-1); } } return(0); } int con_conecta(sConexion *con) { /* Comprobaciones de sanidad */ if(con==NULL || con->host==NULL) return(-1); /* Si ya estábamos conectados, desoconectamos primero */ if(con->enchufe!=-1) close(con->enchufe),con->enchufe=-1; /* Intentamos conectar (bloqueante) */ if((con->enchufe=SockConnect(con->host,con->tam_host,con->puerto))==-1) return(-1); /* escribimos el mensaje de identificacion */ { char msginit[]={"id autozoom/SICO protocol=1.0 display="}; write(con->enchufe,msginit,sizeof(msginit)-1); write(con->enchufe,con->nombre_display, strlen(con->nombre_display)); write(con->enchufe,"\n",1); } /* Limpiamos el búffer de recpcion */ SBufferWipe(con->buf); /* salimos con éxito */ return(0); } int con_escribe(sConexion *con,char *formato,...) { va_list lista; char linea[2048]; /* Comprobaciones de sanidad */ if(con==NULL) return(-1); /* reconectamos si es necesario */ if(con->enchufe==-1) { con_conecta(con); if(con->enchufe==-1) return(-1); } /* Preparamos el texto a mandar */ va_start(lista,formato); vsprintf(linea,formato,lista); va_end(lista); /* Lo mandamos */ write(con->enchufe,linea,strlen(linea)); return(0); } char * con_recibe(sConexion *con) { char buf[1024]; long encolados; int res; if(con==NULL) return(NULL); if(con->enchufe==-1) { con_conecta(con); if(con->enchufe==-1) return(NULL); } if(SockSelect(0,004,con->enchufe,&res,-1)>=0) { SBufferDiscard(con->buf); if((encolados=SockQueued(con->enchufe))<=0) { /* Socket roto */ close(con->enchufe),con->enchufe=-1; return(NULL); } encolados=(encolados>sizeof(buf))?sizeof(buf):encolados; SBufferFill(con->buf,con->enchufe,encolados); } return(SBufferGetLine(con->buf)); }