2013-05-09 23 views
5

Qualche tempo fa ho scritto uno script in C che utilizzava le funzioni API di Windows EnumWindows, SetWindowPos e SetForegroundWindow per disporre automaticamente le finestre (per titolo) in un particolare layout che volevo comunemente.Esiste un equivalente Linux di SetWindowPos?

Esistono equivalenti per Linux per queste funzioni? Userò Kubuntu, quindi le soluzioni specifiche per KDE e/o Ubuntu vanno bene.

+0

Si potrebbe voler usare un wiling di piastrellatura insieme a KDE: http://awesome.naquadah.org/wiki/KDE_and_awesome o http://blog.frozen-zone.org/2010/01/kde4-with-xmonad/ –

risposta

2

Sì, è possibile farlo utilizzando il protocollo X Windows. È un protocollo di livello molto basso, quindi ci vorrà del lavoro. È possibile utilizzare xcb_query_tree per trovare la finestra su cui operare e quindi spostarlo con xcb_configure_window. This page fornisce alcuni dettagli su come farlo. C'è un basic tutorial sull'utilizzo della libreria da cui provengono queste funzioni, ma probabilmente vorrai Google per una migliore.

Può sembrare scoraggiante, ma non è male. Ecco un programma di 50-linea C che si sposta tutti i tuoi xterm 10px a destra:

#include <stdio.h> 
#include <string.h> 
#include <xcb/xcb.h> 

void handle(xcb_connection_t* connection, xcb_window_t window) { 

    xcb_query_tree_reply_t *tree = xcb_query_tree_reply(connection, 
     xcb_query_tree(connection, window), NULL); 
    xcb_window_t *children = xcb_query_tree_children(tree); 

    for (int i = 0; i < xcb_query_tree_children_length(tree); i++) { 

     xcb_get_property_reply_t *class_reply = xcb_get_property_reply(
      connection, 
      xcb_get_property(connection, 0, children[i], XCB_ATOM_WM_CLASS, 
       XCB_ATOM_STRING, 0, 512), NULL); 
     char* class = (char*)xcb_get_property_value(class_reply); 
     class[xcb_get_property_value_length(class_reply)] = '\0'; 

     if (!strcmp(class, "xterm")) { 
      /* Get geometry relative to parent window */ 
      xcb_get_geometry_reply_t* geom = xcb_get_geometry_reply(
       connection, 
       xcb_get_geometry(connection, window), 
       NULL); 

      /* Move 10 pixels right */ 
      uint32_t values[] = {geom->x + 10}; 
      xcb_configure_window(connection, children[i], 
       XCB_CONFIG_WINDOW_X, values); 
     } 

     /* Recurse down window tree */ 
     handle(connection, children[i]); 
    } 
} 

int main() { 
    xcb_connection_t *connection; 
    const xcb_setup_t *setup; 

    connection = xcb_connect(NULL, NULL); 
    setup = xcb_get_setup(connection); 
    xcb_screen_iterator_t screen = xcb_setup_roots_iterator(setup); 
    handle(connection, screen.data->root); 

    return 0; 
} 

Non c'è nessun controllo degli errori o di gestione della memoria, e che cosa può fare è piuttosto limitata. Ma dovrebbe essere semplice aggiornare in un programma che fa ciò che si vuole, o trasformarlo in un programma di aiuto generico aggiungendo opzioni della riga di comando per specificare su quale finestra operare e su quali operazioni eseguire.

+5

@rubenvb \t Se si dispone di una risposta migliore, inserirla con tutti i termini e io inviterò la risposta ed eliminerò questa. Ma non penso che tu ricorrere agli insulti se hai una risposta migliore. – andrewdotn

+1

+1 per fornire un esempio di XCB moderno, è una cosa che il codice legacy continua a utilizzare Xlib ... ma il nuovo codice dovrebbe davvero utilizzare i metodi moderni. O meglio ancora, senza X completamente, ma questo non avrebbe senso dato che l'OP era specificamente su X. – TechZilla

+0

Sono d'accordo con TechZilla, a causa di tutto il codice obsoleto, i nuovi programmatori finiscono per fare le loro cose inizialmente in xlib, e poi rendersi conto che devono passare a xcb a causa di tutti i threading ecc in questi giorni. Ora mi trovo in questa situazione, rifacendo tutto il mio lavoro con xcb, come se avessi un comportamento inspiegabile con xlib. Grazie per questo esempio aiuta così tanto! – Noitidart

2

@andrewdotn ha una buona risposta, ma puoi anche fare questa vecchia scuola semplicemente camminando sull'albero iniziando dalla finestra radice del display usando XQueryTree e recuperando il nome della finestra con XFetchName e spostandolo con XMoveWindow. Ecco un esempio che elencherà tutte le finestre e se sono chiamate "xeyes" vengono spostate in alto a sinistra. Come la maggior parte dei programmi X, c'è dell'altro e probabilmente dovrebbe chiamare XGetWindowProperty per recuperare la proprietà del gestore di finestre esteso _NET_WM_NAME ma l'esempio funziona come un antipasto. Compilare con gcc -Wall -g -o demo demo.c -lX11

#include <X11/Xlib.h> 
#include <stdio.h> 
#include <string.h> 

static int 
EnumWindows(Display *display, Window window, int depth) 
{ 
    Window parent, *children; 
    unsigned int count = 0; 
    int r = 1, n = 0; 
    char *name = NULL; 

    XFetchName(display, window, &name); 
    for (n = 0; n < depth; ++n) putchar(' '); 
    printf("%08x %s\n", (int)window, name?name:"(null)"); 
    if (name && strcmp("xeyes", name) == 0) { 
     XMoveWindow(display, window, 5, 5); 
    } 
    if (name) XFree(name); 

    if (XQueryTree(display, window, &window, &parent, &children, &count) == 0) { 
     fprintf(stderr, "error: XQueryTree error\n"); 
     return 0; 
    } 
    for (n = 0; r && n < count; ++n) { 
     r = EnumWindows(display, children[n], depth+1); 
    } 
    XFree(children); 
    return r; 
} 

int 
main(int argc, char *const argv[]) 
{ 
    Display *display = NULL; 

    if ((display = XOpenDisplay(NULL)) == NULL) { 
     fprintf(stderr, "error: cannot connect to X server\n"); 
     return 1; 
    } 

    EnumWindows(display, DefaultRootWindow(display), 0); 
    XCloseDisplay(display); 
    return 0; 
} 
5

Il modo migliore per farlo è sia nella finestra di gestione stessa (se il vostro supporta le estensioni) oppure con i protocolli e suggerimenti destinati a sostenere "cercapersone" (pager = qualsiasi non-window-manager processo che fa l'organizzazione della finestra o le cose di navigazione).

Le specifiche EWMH includono un _NET_MOVERESIZE_WINDOW progettato per l'uso da cercapersone. http://standards.freedesktop.org/wm-spec/wm-spec-1.3.html#id2731465

Raw Xlib o Xcb è piuttosto approssimativo ma esiste una libreria chiamata libwnck specificatamente progettata per fare il tipo di cosa di cui si sta parlando. (Ho scritto la libreria originale molto tempo fa ma è stata mantenuta da altri per sempre.) Anche se non la usi, leggi il codice per vedere come fare le cose. KDE potrebbe avere un equivalente con le API in stile KDE che non sono sicuro.

Non ci dovrebbe essere bisogno di usare nulla di KDE o GNOME o distribuzione specifica poiché le cose necessarie sono tutte spiegate in EWMH. Detto questo, per alcuni gestori di finestre fare questo come un'estensione potrebbe essere più facile che scrivere un'app separata.

Utilizzare le chiamate X della vecchia scuola direttamente può essere fatto funzionare ma ci sono molti dettagli da gestire che richiedono una notevole esperienza se si vogliono appianare tutti i bug e le casse d'angolo, secondo me, quindi usare un WM API di estensione o libreria di cercapersone sarebbe il mio consiglio.

0

Come sembra che non si stia cercando specificamente una soluzione nel codice, ma piuttosto in un ambiente desktop, è necessario dare un'occhiata a uno dei gestori di finestre che gestiscono il posizionamento della finestra in tale ambiente desktop.

  1. di KDE KWin's Window Attributes

  2. Compiz (GNOME) ha "Regole della finestra" e "Posto Windows" nell'applicazione Impostazioni CompizConfig Manager. Vedi per es. here

  3. Openbox sembra molto più difficile da ottenere correttamente, anche se si collegano a uno strumento GUI nella parte inferiore di this page.

Il problema con l'utilizzo di X è direttamente che X in se stesso non sa nulla circa il vostro ambiente desktop (pannelli, le scorciatoie, ecc) e si dovrà compensare manualmente.

Dopo aver cercato su google per questo, sono sorpreso che KDE sia l'unico che ha un modo semplice per farlo.

Problemi correlati