ubuntuusers.de

Metacity tiling Python

Status: Ungelöst | Ubuntu-Version: Ubuntu 8.10 (Intrepid Ibex)
Antworten |

zhocchao

Anmeldungsdatum:
5. Mai 2008

Beiträge: 65

Hallo

Gibt es hier jemanden, der Lust und genug Kenntnisse hat, um mit mir ein Tiling-Windows-Placement-Metacity-Dingens zu entwickeln?

(Python, da ich dann wenigstens rudimentär zur Programmierung beitragen kann).

Bitte PN oder hier melden

Danke Z

fred.reichbier

Anmeldungsdatum:
14. Dezember 2006

Beiträge: 350

Hallo,

ich vermute mal, du willst einen Window Manager, der die Features von Metacity und Tiling beherrscht. Rein zufällig arbeite ich bei samurai-x mit, das ist ein sich im Aufbau befindlicher sehr erweiterbarer Window Manager auf Pythonbasis (während sich die Version im Repo aber grundsätzlich von dem Release 0.1 unterscheidet, weil wir seit 0.1 das Hauptziel der Erweiterbarkeit haben 😉. Gestern habe ich auch ein (experimentelles, aber immerhin funktionierts grundsätzlich) Tiling-Plugin committet (sx-tiling nennt sich das). Samurai-x hat noch nicht alle Features von Metacity (insbesondere beim Fokus und bei den Fensterdekorationen fehlt da noch einiges), und auch nicht alle von anderen Tiling-WMs, aber das kann ja noch werden. Grundsätzlich benutzbar ist er jetzt aber schon. Um samurai-x auszuprobieren, müsstest du im Grunde einfach das git-Repository auschecken und die Packages innerhalb von ooxcb und samurai-x2 installieren (andere Abhängigkeiten sind libxcb1 und die python-setuptools). Dann hast du schon sx-wm, das startet einen 'nackten' samurai-x. Mehr Funktionalitäten kommen da durch Plugins rein. Die Installationsanweisungen für die Plugins stehen in HACKING.rst. Eine eigene Konfigurationsdatei kannst du durch sx-wm --default-config > ~/.samuraix/config anlegen.

Also - falls du es mal ausprobieren willst ... Hilfe könnten wir auch noch gebrauchen ☺

Der Vollständigkeit halber: Es gäbe aber auch noch andere Python - Window Manager im Aufbau - zum Beispiel whimsy oder parti.

Sorry, falls ich da ein bisschen zu viel Werbung gemacht haben sollte 😉

Gruß,

Friedrich

zhocchao

(Themenstarter)

Anmeldungsdatum:
5. Mai 2008

Beiträge: 65

Hallo

Erstmal danke für Deine Antwort. Ich habe die Angelegenheit "ein wenig" verkürzt dargestellt. Prinzipiell finde ich das Samurai-x Projekt interessant. Allerdings überfordert mich das sowohl zeitlich als auch in meinen Fähigkeiten. Grundsätzlich reicht mir Metacity.

Ich hab mir ein Bash script gebastelt, welches mir meine Fenster je nach Anzahl nach einem bestimmten Muster verteilt. Mit der Zeit kam noch eins dazu, welches das aktive Fenster verkleinert auf die Seite schiebt (je nach Titel an eine bestimmte Stelle/Größe). So langsam komme ich aber an die Grenzen der Bash.

- Ich dachte daran Regeln für verschiedene Fenster anlegen (wo sie beim tiling bevorzugt abgelegt werden).
- Verschiedene Tiligmuster zur Auswahl je nach Anzahl der Fenster
- Vielleicht ne GUI um das Tilingmuster festzulegen
- Vielleicht verschiedene Standardgrößen für Fenster
...
Also ein Programm, welches sich nur mit Fenstergröße und -position beschäftigt. Im Endeffekt halte ich das nicht für allzu aufwendig. Wenn ich das aber selbst zusammenfrickel kommt dabei Code des Grauens raus und ich würde das lieber gleich anständig angehen, statt in 1,2 Jahren das ganze neu schreiben zu müssen.

g

z

snafu1

Avatar von snafu1

Anmeldungsdatum:
5. September 2007

Beiträge: 2133

Wohnort: Gelsenkirchen

Ich hatte gehofft, dies über die im anderen Thread angesprochenen Xlib-Bindings für Python zu schaffen. Das heißt: Die Größe bestehender Fenster zu manipulieren, ohne dafür einen eigenen Windowmanager programmieren zu müssen.

Ich dachte, man könnte hierfür configure() auf ein bestehendes Fenster anwenden. Leider tut sich beim Aufruf meiner Funktion tile() rein gar nichts - keine Größenänderung, keine Fehlermeldung. ☹ (die Positionierung hätte ich später eingebaut)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
In [1]: from windowcontrol import WinControl

In [2]: wc = WinControl()

In [3]: for xidname in wc.get_xids(wins=True): print xidname
   ...: 
(37748940, 'Neue Antwort in \xe2\x80\x9eMetacity tiling Python\xe2\x80\x9c \xe2\x80\xba Forum \xe2\x80\xba ubuntuusers.de - Mozilla Firefox 3.1 Beta 2')
(44040224, 'Terminal - ~')
(41943097, 'windowcontrol.py - Bluefish 1.0.7')

In [4]: wc.tile(41943097, 44040224) # hier passiert leider nichts

Ich poste mal den Code, vielleicht findet sich ja noch Hilfe...

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#!/usr/bin/env python

from Xlib.display import Display
from Xlib.X import AnyPropertyType


class WinControl(object):

    def __init__(self):
        self.display = Display()
        self.screen = self.display.screen()
        self.CLIENTLIST = self.display.intern_atom('_NET_CLIENT_LIST')
        self.NETNAME = self.display.intern_atom('_NET_WM_NAME')
        self.NORMAL = self.display.intern_atom('_NET_WM_WINDOW_TYPE_NORMAL')
        self.WINTYPE = self.display.intern_atom('_NET_WM_WINDOW_TYPE')

    def get_xids(self, wins=False):
        """Return all xids from clientlist
        Setting 'wins' returns only window xids""" 
        root = self.screen.root
        xids = root.get_full_property(self.CLIENTLIST, AnyPropertyType).value
        for xid in xids:
            xidname = (int(xid), self.get_name(xid))
            if wins:
                if self.iswindow(xid):
                    yield xidname
            else:
                yield xidname
                
    def get_name(self, xid):
        window = self.get_window(xid)
        netname = window.get_full_property(self.NETNAME, AnyPropertyType).value
        return window.get_wm_name() or netname

    def iswindow(self, xid):
        window = self.get_window(xid)
        wintype = window.get_full_property(self.WINTYPE, AnyPropertyType).value
        return self.NORMAL in wintype

    def get_window(self, xid):
        return self.display.create_resource_object('window', xid)

    def tile(self, left, right):
        "Tile two windows verticale using their xids"
        winwidth = self.screen.width_in_pixels/2
        winheight = self.screen.height_in_pixels
        win_left, win_right = self.get_window(left), self.get_window(right)
        win_left.configure(width=winwidth, height=winheight)
        win_right.configure(width=winwidth, height=winheight)



def main():
    "Get all open window names and their id's in xprop style (hexadecimal)"
    winxid = WinXID()
    for xid, name in winxid.get_xids(only_wins=True):
        print hex(xid), name


if __name__ == '__main__':
    main()

fred.reichbier

Anmeldungsdatum:
14. Dezember 2006

Beiträge: 350

Im Zweifelsfall fehlt da einfach ein self.display.flush() nach den configures. Besser wäre es vielleicht, die _NET_MOVERESIZE_WINDOW - ClientMessage zu nutzen. Ich hab dafür hier was 😀

Gruß,

Fred

Romario

Avatar von Romario

Anmeldungsdatum:
7. Februar 2008

Beiträge: 323

Wohnort: Köln

Auch wenn jetzt schon viele Lösungsansätze vorliegen...

Hier mal ein quick&dirty Vorschlag in Perl und libwnck (Window Navigator Construction Kit library):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#! /usr/bin/perl

use strict;
use warnings;
use Gtk2 '-init';	
use Gnome2::Wnck;

my $screen = Gnome2::Wnck::Screen->get_default;
$screen->force_update();

foreach my $win ( $screen->get_windows ) {
	
	#nicht die Größe der Gnome-Panel ändern ;-)
	next if $win->get_name =~ /Panel/;
	
	#bei allen übrigen Fenstern auf dem aktuellen Workspace passen wir die Breite an
	print $win->get_name."\n";
	
	#Maximierte Fenster nehmen keine Größenänderung an...
	$win->unmaximize;
	
	#Hinweis: nicht alle Fenster lassen sich auf jede beliebige Größe setzen
	#die erreichte Größe könnte man anschließend durch $win->get_geometry kontrollieren
	$win->set_geometry ('current', 'width', 0, 0, 320, 280);
	
}

1;

Installation der nötigen Abhängigkeiten, um das Skript auszuführen:

1
sudo apt-get install libgtk2-perl libgnome2-wnck-perl

snafu1

Avatar von snafu1

Anmeldungsdatum:
5. September 2007

Beiträge: 2133

Wohnort: Gelsenkirchen

flush() hilft mir schon sehr gut weiter. Die Fenster werden jetzt in ihrer Größe angepasst und verschoben.

Probleme:

- Rechtes Fenster liegt unter linkem statt daneben (habe ich einen Denkfehler oder funktioniert x und y nicht richtig?)

- Fenster liegen unter Panels (was klar ist, weil die Bildschirmauflösung das eben auch beinhaltet, wird sich aber wohl lösen lassen)

- Rechtes Fenster, wenn man es dann von Hand neben das linke zieht, geht über Bildschrimrand hinaus (warum, weiß ich nicht)

- Vollbildfenster werden nicht berührt (muss man halt vorher in "Normalzustand" setzen, sollte auch nicht so schwierig sein)

@zhocchao:

Wie sieht's denn aus? Besteht Interesse an einem Fenstermanager-unabhängigen Tiler, der direkt mit dem X-Server kommuniziert und dort die Anweisungen zum Fensterverschieben gibt? Ich persönlich will sowas schon länger machen. Bin mir auch gar nicht sicher, ob es sowas schon gibt (also ein Tiler, der kein eigener WM ist). Ich glaube nämlich nicht. Der Ansporn ist aber eben größer, wenn man zwischendurch ein bißchen Feedback kriegt, oder so Leute wie Fred dabei hat, die einem helfen. 😉

Achso, hier noch die aktuelle Funktion. Wenn einer weiß, was ich falsch mach, bitte sagen. 😉

1
2
3
4
5
6
7
    def tile(self, left, right):
        "Tile two windows verticale using their xids"
        w, h = self.screen.width_in_pixels/2, self.screen.height_in_pixels
        win_left, win_right = self.get_window(left), self.get_window(right)
        win_left.configure(width=w, height=h, x=0, y=0)
        win_right.configure(width=w, height=h, x=0, y=w/2)
        self.display.flush()

fred.reichbier

Anmeldungsdatum:
14. Dezember 2006

Beiträge: 350

Hallo snafu1,

ich glaub, da ist ein kleiner Denkfehler drin. Für mich siehts so aus, als hättest du bei den zwei Configure-Aufrufen beide Male x und y verwechselt ... x ist waagerecht, y ist senkrecht 😉

Fenster vor andere schieben kannst du ebenfalls mit configure (das Argument stack_mode), und da gibt es unter anderem X.Above.

Übrigens ist durchaus auch das Xlib-Manual der C-Version zu empfehlen, das erklärt nämlich einiges ziemlich gut 😉

Ich habe aber den Eindruck, dass die Lösung per configure eher weniger schön ist, weil das hinter dem Rücken des Window Managers passiert. Ich tät empfehlen, wo es geht, die im netwm-Standard beschriebenen client messages zu benutzen. Aber fürs Erste reicht Configure bestimmt aus ☺

Der Ansporn ist aber eben größer, wenn man zwischendurch ein bißchen Feedback kriegt, oder so Leute wie Fred dabei hat, die einem helfen. 😉

Gerne 😀

Gruß,

Fred

zhocchao

(Themenstarter)

Anmeldungsdatum:
5. Mai 2008

Beiträge: 65

@snafu1

Klar besteht da Interesse, Ich hatte danach gesucht, aber nichts gefunden. Ich hab ja schon geschrieben, was ich mir so gedacht hab. Ich werde mich nach meinen Möglichkeiten beteiligen. Momentan hab ich versucht durch eure Beispiele und Posts durchzusteigen, was mir nicht so gelungen ist.

z

snafu1

Avatar von snafu1

Anmeldungsdatum:
5. September 2007

Beiträge: 2133

Wohnort: Gelsenkirchen

Ich habe das (noch sehr in seinen Anfängen befindliche) Tiling jetzt als Option -t bzw --tile von der Kommandozeile eingebaut. Einfach vorher alle Fenster auf "wiederhergestellt" setzen und gucken was passiert. ☺

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#!/usr/bin/env python

from optparse import OptionParser
import sys

from Xlib.display import Display
from Xlib.X import AnyPropertyType


class WinControl(object):

    def __init__(self):
        self.display = Display()
        self.screen = self.display.screen()
        self.CLIENTLIST = self.display.intern_atom('_NET_CLIENT_LIST')
        self.NETNAME = self.display.intern_atom('_NET_WM_NAME')
        self.NORMAL = self.display.intern_atom('_NET_WM_WINDOW_TYPE_NORMAL')
        self.WINTYPE = self.display.intern_atom('_NET_WM_WINDOW_TYPE')

    def get_xids(self, only_xids=False, wins=False, max=-1):
        """Return all xids from clientlist
        
        xids: return only xids (no names)
        wins: Return only window xids
        max:  Number of maximal returned xids, -1 means no maximum
        """ 
        root = self.screen.root
        xids = root.get_full_property(self.CLIENTLIST, AnyPropertyType).value
        if wins:
            xids = [xid for xid in xids if self.iswindow(xid)]
        if max == -1:
            max = len(xids)
        for i in xrange(max):
            xid = xids[i]
            if only_xids:
                yield int(xid)
            else:
                yield int(xid), self.get_name(xid)
                
    def get_name(self, xid):
        window = self.get_window(xid)
        netname = window.get_full_property(self.NETNAME, AnyPropertyType).value
        return window.get_wm_name() or netname

    def iswindow(self, xid):
        window = self.get_window(xid)
        wintype = window.get_full_property(self.WINTYPE, AnyPropertyType).value
        return self.NORMAL in wintype

    def get_window(self, xid):
        return self.display.create_resource_object('window', xid)

    def tile(self, left, right):
        """Tile two windows verticale using their xids
        """
        w, h = self.screen.width_in_pixels, self.screen.height_in_pixels
        win_left, win_right = self.get_window(left), self.get_window(right)
        win_left.configure(width=w/2, height=h, x=0, y=0)
        win_right.configure(width=w/2, height=h, x=w/2, y=0)
        self.display.flush()


def main():
    """Get all open window names and their ids in xprop style (hexadecimal)
    
    Option '-t' / '--tile':
    Tile first two windows of clientlist instead of showing id's
    """
    parser = OptionParser(
        'Usage: %prog [-t|--tile] [-h|--help]\n' \
        'Get all open window names and their ids in xprop style (hexadecimal)')

    parser.add_option(
        '-t',
        '--tile',
        dest='tile',
        action='store_true',
        help="tile first two windows of clientlist instead of showing id's")

    (options, args) = parser.parse_args()

    wc = WinControl()

    if options.tile:
        try:
            left, right = wc.get_xids(only_xids=True, wins=True, max=2)
        except IndexError:
            print >> sys.stderr, 'Could not tile: Found less than two windows'
            sys.exit(1)
        wc.tile(left, right)

    else:
        for xid, name in wc.get_xids(wins=True):
            print hex(xid), name


if __name__ == '__main__':
    main()

snafu1

Avatar von snafu1

Anmeldungsdatum:
5. September 2007

Beiträge: 2133

Wohnort: Gelsenkirchen

Romario schrieb:

Hier mal ein quick&dirty Vorschlag in Perl und libwnck (Window Navigator Construction Kit library)

Sieht natürlich wesentlich einfacher aus, wenn man ein Toolkit benutzt. Allerdings möchte vielleicht nicht jeder User die kompletten Gnome-Bibliotheken samt Abhängigkeiten installieren, nur um seine Fenster teilen zu können. Man denke mal an Xubuntu-, Kubuntu- und User ganz anderer WM's.

Okay, Aufgabenstellung des OP's war Metacity. Von daher könnte die Sache auch so gelöst werden. Sie entspricht dann allerdings nicht mehr meinem Vorhaben... 😉

fred.reichbier schrieb:

Ich habe aber den Eindruck, dass die Lösung per configure eher weniger schön ist, weil das hinter dem Rücken des Window Managers passiert. Ich tät empfehlen, wo es geht, die im netwm-Standard beschriebenen client messages zu benutzen.

Welche Nachteile hätte das? Ich hatte vermutet, dass configure() genau dies tät und es daher unnötig wäre, das ganze nochmal selbst zu implementieren.

zhocchao

(Themenstarter)

Anmeldungsdatum:
5. Mai 2008

Beiträge: 65

snafu1 schrieb:

Sieht natürlich wesentlich einfacher aus, wenn man ein Toolkit benutzt. Allerdings möchte vielleicht nicht jeder User die kompletten Gnome-Bibliotheken samt Abhängigkeiten installieren, nur um seine Fenster teilen zu können. Man denke mal an Xubuntu-, Kubuntu- und User ganz anderer WM's.

Okay, Aufgabenstellung des OP's war Metacity. Von daher könnte die Sache auch so gelöst werden. Sie entspricht dann allerdings nicht mehr meinem Vorhaben... 😉

Ich hab mal e17 ausprobiert, und hätte es auch weiter verwendet, allerdings konnte ich da mit wmctrl nicht den Desktop ermitteln, mein Bash script hat dort nicht funktioniert und die e17 tiling Option taugt nix. Insofern finde ich persönlich ein unabhängiges Prog vorteilhafter.

fred.reichbier

Anmeldungsdatum:
14. Dezember 2006

Beiträge: 350

snafu1: configure sitzt sozusagen eine Ebene tiefer, das ist eine elementare X-Funktion. An fremden Fenster rumzuconfiguren geht anscheinend zwar gut, und der Window Manager sollte das auch verkraften, aber schön ist das nicht unbedingt 😉 Das hier wäre dann sauberer, weil der Window Manager dann entscheiden darf, ob er das tut oder nicht. Andererseits unterstützt _NET_MOVERESIZE_WINDOW vermutlich nicht jeder Window Manager – vielleicht wärs also doch am besten, configure zu nutzen, solange keine Probleme auftauchen 😀 Ich könnte mir vorstellen, dass auch libwnck irgendwo an seine Grenzen stößt ... mit der Xlib ist man zwar etwas mehr low-level, hat aber dafür die größere Kontrolle. Ich hab sowas aber auch noch nicht gemacht, kann ich also nicht mit Sicherheit sagen 😉

Gruß,

Fred

Romario

Avatar von Romario

Anmeldungsdatum:
7. Februar 2008

Beiträge: 323

Wohnort: Köln

snafu1 schrieb:

Romario schrieb:

Hier mal ein quick&dirty Vorschlag in Perl und libwnck (Window Navigator Construction Kit library)

Sieht natürlich wesentlich einfacher aus, wenn man ein Toolkit benutzt. Allerdings möchte vielleicht nicht jeder User die kompletten Gnome-Bibliotheken samt Abhängigkeiten installieren, nur um seine Fenster teilen zu können. Man denke mal an Xubuntu-, Kubuntu- und User ganz anderer WM's.

Bei den verwendeten Bibliotheken besteht keine Abhängigkeit zu irgendwelchen Gnome Bibliotheken. Man darf sich hier nicht vom Namen (es ist lediglich Teil des Gnome Projektes) täuschen lassen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
mario@romario:~$ sudo apt-cache depends libgnome2-wnck-perl 
libgnome2-wnck-perl
  Hängt ab: perl
  Hängt ab: <perlapi-5.10.0>
    perl-base
  Hängt ab: libatk1.0-0
  Hängt ab: libc6
  Hängt ab: libcairo2
  Hängt ab: libfontconfig1
  Hängt ab: libfreetype6
  Hängt ab: libglib2.0-0
  Hängt ab: libgtk2.0-0
  Hängt ab: libpango1.0-0
  Hängt ab: libwnck22
  Hängt ab: zlib1g
  Hängt ab: libglib-perl
  Hängt ab: libgtk2-perl

Okay, Aufgabenstellung des OP's war Metacity. Von daher könnte die Sache auch so gelöst werden. Sie entspricht dann allerdings nicht mehr meinem Vorhaben... 😉

War ja auch nur als Anregung gedacht.

snafu1

Avatar von snafu1

Anmeldungsdatum:
5. September 2007

Beiträge: 2133

Wohnort: Gelsenkirchen

@Romario:

Okay, da lag ich falsch. Das sind ja tatsächlich nur ein paar Megabyte (wenn man einige der Abhängigkeiten mitrechnet).

Also ich hänge nicht an der X-Lib... Wenn du Lust hast, kannst du das ganze ja noch etwas verfeinern. Ich persönlich will eigentlich nur endlich mal nen Tiler unter Xfce nutzen können.

Und der OP hat ja ohnehin für später mal eine Gui in Betracht gezogen, die man dann auch in Gtk machen könnte, wenn man will.

Habe dein Skript jetzt mal getestet. Die Probleme mit den Panels bestehen da leider auch.

Nun auch wnck für Python getestet: Schien auf dem ersten Blick optimal, da viel einfacher. Dummerweise klappt aber set_geometry() bei mir nicht (im Xlib Skript via configure() aber schon). Die anderen Sachen wie maximize(), get_name() usw funktionieren. Da diese Funktion aber der Kern des Projektes ist, finde ich das etwas ungünstig... 😉

Sofern es in der Hinsicht keine Lösung gibt, würde ich schon lieber bei der Xlib bleiben. Die wichtigen Sachen sind ja auch allmählich implementiert. Normalerweise sollte das nun folgende einfache Mathematik sein.

Antworten |