コード例 #1
0
class Title(Widget):

    dispatcher = dependency(CommandDispatcher, 'commander')
    theme = dependency(Theme, 'theme')

    stretched = True

    def __zorro_di_done__(self):
        bar = self.theme.bar
        self.color = bar.text_color_pat
        self.font = bar.font
        self.padding = bar.text_padding
        self.dispatcher.events['window'].listen(self.window_changed)
        self.oldwin = None

    def window_changed(self):
        if self.oldwin is not None:
            self.oldwin.property_changed.unlisten(self.bar.redraw.emit)
        win = self.dispatcher.get('window', None)
        if win is not None:
            win.property_changed.listen(self.bar.redraw.emit)
        self.oldwin = win
        self.bar.redraw.emit()

    def draw(self, canvas, l, r):
        win = self.dispatcher.get('window', None)
        if not win:
            return r, r
        canvas.set_source(self.color)
        self.font.apply(canvas)
        canvas.move_to(l + self.padding.left,
                       self.height - self.padding.bottom)
        canvas.show_text(get_title(win) or '')
        return r, r
コード例 #2
0
ファイル: dashboards.py プロジェクト: tailhook/fedor
class DashboardHTTP(HTTPService):

    jinja = dependency(Jinja, 'jinja')
    redis = dependency(Redis, 'redis')

    @public
    def default(self, uri):
        ident = uri[2:]
        leftcol = []
        rightcol = []
        data = self.redis.execute('GET', 'dashboard:{}:notepads'.format(ident))
        if data:
            data = json.loads(data.decode('utf-8'))
            inject = di(self).inject
            for kind, uri in data:
                note = inject(Notepad.from_url(uri))
                if kind == 'left':
                    leftcol.append(note)
                elif kind == 'right':
                    rightcol.append(note)
                else:
                    raise NotImplementedError(kind)
        return (b'200 OK', b'Content-Type\0text/html; charset=utf-8\0',
                self.jinja.get_template('dashboard.html').render(
                    title=uri_to_title(ident),
                    left_column=leftcol,
                    right_column=rightcol,
                ))
コード例 #3
0
ファイル: groupbox.py プロジェクト: metulburr/tilenol
class State(object):

    commander = dependency(CommandDispatcher, 'commander')
    gman = dependency(GroupManager, 'group-manager')

    def __init__(self):
        self._state = None

    def dirty(self):
        return self._state != self._read()

    def update(self):
        nval = self._read()
        if nval != self._state:
            self._state = nval
            return True

    def _read(self):
        cur = self.commander.get('group')
        visgr = self.gman.current_groups.values()
        return tuple(
            GroupState(g.name, g.empty, g is cur, g in visgr,
                       g.has_urgent_windows) for g in self.gman.groups)

    @property
    def groups(self):
        return self._state
コード例 #4
0
class Icon(Widget):

    dispatcher = dependency(CommandDispatcher, 'commander')
    theme = dependency(Theme, 'theme')

    def __zorro_di_done__(self):
        self.padding = self.theme.bar.box_padding
        self.dispatcher.events['window'].listen(self.window_changed)
        self.oldwin = None

    def window_changed(self):
        if self.oldwin is not None:
            self.oldwin.property_changed.unlisten(self.bar.redraw.emit)
        win = self.dispatcher.get('window', None)
        if win is not None:
            win.property_changed.listen(self.bar.redraw.emit)
        self.oldwin = win
        self.bar.redraw.emit()

    def draw(self, canvas, l, r):
        win = self.dispatcher.get('window', None)
        if not win or not getattr(win, 'icons', None):
            return l, r
        h = self.height - self.padding.bottom - self.padding.top
        if self.right:
            x = r - self.padding.right - h
        else:
            x = l + self.padding.left
        win.draw_icon(canvas, x, self.padding.top, h)
        if self.right:
            return l, r - h - self.padding.left - self.padding.right
        else:
            return l + h + self.padding.left + self.padding.right, r
コード例 #5
0
ファイル: commands.py プロジェクト: metulburr/tilenol
class EmulCommands(object):

    keyregistry = dependency(object, 'key-registry')  # circ dependency
    commander = dependency(CommandDispatcher, 'commander')
    xcore = dependency(Core, 'xcore')

    def cmd_key(self, keystr):
        mod, sym = self.keyregistry.parse_key(keystr)
        code = self.xcore.keysym_to_keycode[sym][0]
        self.xcore.xtest.FakeInput(
            type=2,
            detail=code,
            time=0,
            root=self.xcore.root_window,
            rootX=0,
            rootY=0,
            deviceid=0,
            )
        self.xcore.xtest.FakeInput(
            type=3,
            detail=code,
            time=100,
            root=self.xcore.root_window,
            rootX=0,
            rootY=0,
            deviceid=0,
            )

    def cmd_button(self, num):
        num = int(num)
        self.xcore.xtest.FakeInput(
            type=4,
            detail=num,
            time=0,
            root=self.xcore.root_window,
            rootX=0,
            rootY=0,
            deviceid=0,
            )
        self.xcore.xtest.FakeInput(
            type=5,
            detail=num,
            time=30,
            root=self.xcore.root_window,
            rootX=0,
            rootY=0,
            deviceid=0,
            )
コード例 #6
0
class RenameWindow(Select):

    commander = dependency(CommandDispatcher, 'commander')

    def items(self):
        win = self._target_window
        titles = [
            win.props.get("_NET_WM_VISIBLE_NAME"),
            win.props.get("_NET_WM_NAME"),
            win.props.get("WM_NAME"),
            win.props.get("WM_ICON_NAME"),
            win.props.get("WM_CLASS").replace('\0', ' '),
            win.props.get("WM_WINDOW_ROLE"),
        ]
        res = []
        for t in titles:
            if not t: continue
            if res and res[-1][0] == t: continue
            res.append((t, t))
        return res

    def submit(self, input, matched, value):
        self._target_window.set_property('_NET_WM_VISIBLE_NAME', input)

    def cmd_show(self):
        self._target_window = self.commander['window']
        super().cmd_show()

    def _close(self):
        super()._close()
        if hasattr(self, '_target_window'):
            del self._target_window

    def cmd_clear_name(self):
        self.commander['window'].set_property('_NET_WM_VISIBLE_NAME', None)
コード例 #7
0
class Notepad(object):

    redis = dependency(Redis, 'redis')

    def __init__(self, ident, title):
        self.ident = ident
        self.title = title
        self.records_key = ("notepad:{}:records".format(self.ident))\
            .encode('ascii')
        self.topic = ("notepad:{}".format(self.ident)).encode('ascii')

    @classmethod
    def from_url(cls, url):
        return cls(url[1:], uri_to_title(url))

    @classmethod
    def from_id(cls, ident):
        return Notepad(ident, None)

    @cached
    def records(self):
        # TODO(pc) cache
        recs = self.redis.execute("SORT", self.records_key, "BY", "nokey",
                                  "GET", "record:*")
        return [json.loads(rec.decode('utf-8')) for rec in recs]

    def new_record(self, title):
        title = title.strip()
        recid = self.redis.execute('INCR', 'record_counter')
        rec = {
            'id': recid,
            'title': title,
        }
        return rec
コード例 #8
0
ファイル: screen.py プロジェクト: metulburr/tilenol
class ScreenManager(object):

    commander = dependency(CommandDispatcher, 'commander')

    def __init__(self, rectangles):
        self.screens = []
        for i, rect in enumerate(rectangles):
            scr = Screen()
            scr.set_bounds(rect)
            self.screens.append(scr)

    def __zorro_di_done__(self):
        inj = di(self)
        for i, scr in enumerate(self.screens):
            inj.inject(scr)
            self.commander['screen.{}'.format(i)] = scr

    def update(self, screens):
        # TODO(tailhook) try to guess which one turned off
        while len(self.screens) > len(screens):
            scr = self.screens.pop()
            idx = len(self.screens)
            del self.commander['screen.{}'.format(idx)]
        for i, s in enumerate(self.screens):
            s.set_bounds(screens[i])
        while len(self.screens) < len(screens):
            idx = len(self.screens)
            scr = Screen()
            scr.set_bounds(screens[idx])
            self.screens.append(scr)
            self.commander['screen.{}'.format(idx)] = scr
コード例 #9
0
class DefaultHandler(JSONWebsockInput):

    output = dependency(JSONWebsockOutput, 'websock_output')

    def handle_connect(self, cid):
        self.output.add_output(cid, '["notepad.', 'notepad')
        self.output.add_output(cid, '["dashboard.', 'dashboard')
コード例 #10
0
class SelectLayout(Select):

    config = dependency(Config, 'config')

    def items(self):
        return sorted(self.config.all_layouts().items(), key=itemgetter(0))

    def submit(self, input, matched, value):
        self.commander['group'].cmd_set_layout(matched)
コード例 #11
0
class NotepadHTTP(HTTPService):

    jinja = dependency(Jinja, 'jinja')

    @public
    def default(self, uri):
        note = di(self).inject(Notepad.from_url(uri))
        return (b'200 OK', b'Content-Type\0text/html; charset=utf-8\0',
                self.jinja.get_template('notepad.html').render(notepad=note))
コード例 #12
0
class Tabs(GadgetBase):

    screens = dependency(ScreenManager, 'screen-manager')
    commander = dependency(CommandDispatcher, 'commander')

    def __init__(self, width=256, groups=()):
        self.bars = {}
        self.groups = set(groups)
        self.width = width
        self.states = {}

    def __zorro_di_done__(self):
        for s in self.screens.screens:
            bar = di(self).inject(
                LeftBar(s, self.width, self.groups, self.states))
            self.bars[s] = bar
            if s.group.name in self.groups:
                s.slice_left(bar)

    def cmd_toggle(self):
        gr = self.commander['group']
        if gr.name in self.groups:
            self.groups.remove(gr.name)
        else:
            self.groups.add(gr.name)
        self._update_bars()

    def cmd_show(self):
        gr = self.commander['group']
        self.groups.add(gr.name)
        self._update_bars()

    def cmd_hide(self):
        if gr.name in self.groups:
            self.groups.remove(gr.name)
        self._update_bars()

    def _update_bars(self):
        for s, bar in self.bars.items():
            if s.group.name in self.groups:
                bar.show()
            else:
                bar.hide()
コード例 #13
0
ファイル: ewmh.py プロジェクト: metulburr/tilenol
class Ewmh(object):

    xcore = dependency(Core, 'xcore')
    dispatcher = dependency(object, 'event-dispatcher')

    def __zorro_di_done__(self):
        self.window = Window(
            self.xcore.create_toplevel(Rectangle(0, 0, 1, 1),
                                       klass=self.xcore.WindowClass.InputOnly,
                                       params={}))
        di(self).inject(self.window)
        self.xcore.raw.ChangeProperty(
            window=self.xcore.root_window,
            mode=self.xcore.PropMode.Replace,
            property=self.xcore.atom._NET_SUPPORTING_WM_CHECK,
            type=self.xcore.atom.WINDOW,
            format=32,
            data_len=1,
            data=struct.pack('<L', self.window))
        self.window.set_property('_NET_SUPPORTING_WM_CHECK', self.window)
        self.window.set_property('_NET_WM_NAME', 'tilenol')

    def showing_window(self, win):
        self.xcore.raw.ChangeProperty(window=win,
                                      mode=self.xcore.PropMode.Replace,
                                      property=self.xcore.atom.WM_STATE,
                                      type=self.xcore.atom.CARD32,
                                      format=32,
                                      data_len=2,
                                      data=struct.pack('<LL', 1, 0))

    def hiding_window(self, win):
        self.xcore.raw.ChangeProperty(
            window=win,
            mode=self.xcore.PropMode.Replace,
            property=self.xcore.atom.WM_STATE,
            type=self.xcore.atom.CARD32,
            format=32,
            data_len=2,
            data=struct.pack('<LL', 0, 0),
            _ignore_error=True)  # window already destroyed
コード例 #14
0
class FindWindow(Select):

    commander = dependency(CommandDispatcher, 'commander')

    def items(self):
        items = []
        for g in self.commander['groups'].groups:
            for win in g.all_windows:
                t = (get_title(win) or win.props.get('WM_ICON_NAME')
                     or win.props.get('WM_CLASS'))
                items.append((t, win))
        return sorted(items, key=itemgetter(0))

    def submit(self, input, matched, value):
        self.commander['groups'].cmd_switch(value.group.name)
コード例 #15
0
class State(object):

    commander = dependency(CommandDispatcher, 'commander')

    def __init__(self, gr):
        self._state = None
        self._group = gr

    def dirty(self):
        return self._state != self._read()

    def update(self):
        nval = self._read()
        if nval != self._state:
            self._state = nval
            return True

    def _read(self):
        cur = self.commander.get('window')
        gr = self._group
        subs = list(gr.current_layout.sublayouts())
        res = []
        for sec in subs:
            wins = sec.windows
            if wins:
                title = getattr(sec, 'title', sec.__class__.__name__)
                sect = [title]
                for win in wins:
                    sect.append(self._winstate(win, cur))
                res.append(sect)
        if gr.floating_windows:
            sect = ['floating']
            for win in gr.floating_windows:
                sect.append(self._winstate(win, cur))
        return res

    def _winstate(self, win, cur):
        return WindowState(
            title=get_title(win) or win.props.get("WM_CLASS") or hex(win),
            icon=getattr(win, 'icons', None),
            active=win is cur,
            urgent=is_window_urgent(win),
            win=win,
        )

    @property
    def sections(self):
        return self._state
コード例 #16
0
ファイル: clock.py プロジェクト: metulburr/tilenol
class Clock(Widget):

    theme = dependency(Theme, 'theme')

    def __init__(self, *, format="%H:%M:%S %d.%m.%Y", right=False):
        super().__init__(right=right)
        self.format = format

    def __zorro_di_done__(self):
        bar = self.theme.bar
        self.font = bar.font
        self.color = bar.text_color_pat
        self.padding = bar.text_padding
        gethub().do_spawnhelper(self._update_time)

    def _update_time(self):
        while True:
            tts = 1.0 - (time.time() % 1.0)
            if tts < 0.001:
                tts = 1
            sleep(tts)
            self.bar.redraw.emit()

    def _time(self):
        return datetime.datetime.now().strftime(self.format)

    def draw(self, canvas, l, r):
        self.font.apply(canvas)
        canvas.set_source(self.color)
        tm = self._time()
        _, _, w, h, _, _ = canvas.text_extents(tm)
        if self.right:
            x = r - self.padding.right - w
            r -= self.padding.left + self.padding.right + w
        else:
            x = l + self.padding.left
            l += self.padding.left + self.padding.right + w
        canvas.move_to(x, self.height - self.padding.bottom)
        canvas.show_text(tm)
        return l, r
コード例 #17
0
class Split(Layout):
    """Split layout

    It's customized by subclassing, not by instantiating. Class definition
    should consist of at least one stack

    :var fixed: whether to skip empty stacks or reserve place for them
    """
    fixed = False
    vertical = True

    commander = dependency(CommandDispatcher, 'commander')

    def __init__(self):
        super().__init__()
        self.auto_stacks = []
        self.stack_list = []
        self.stacks = {}
        for stack_class in self.get_defined_classes(BaseStack).values():
            stack = stack_class(self)
            self.stacks[stack.__class__.__name__] = stack
            self.stack_list.append(stack)
            if stack.priority is not None:
                self.auto_stacks.append(stack)
        self.auto_stacks.sort(key=lambda s: s.priority)

    def set_bounds(self, bounds):
        self.bounds = bounds
        self.dirty()

    def _assign_boxes(self, box):
        if self.fixed:
            all_stacks = self.stack_list
        else:
            all_stacks = [s for s in self.stack_list if not s.empty]
        curw = 0
        if self.vertical:
            rstart = start = box.x
            totalpx = box.width
        else:
            rstart = start = box.y
            totalpx = box.height
        totpx = sum((s.size or s.min_size) for s in all_stacks)
        totw = sum(s.weight for s in all_stacks if s.size is None)
        skip_pixels = totpx > totalpx or (not totw and totpx != totalpx)
        if skip_pixels:
            totw = sum(s.weight for s in all_stacks)
        else:
            totalpx -= sum(s.size for s in all_stacks if s.size is not None)
        pxoff = 0
        for s in all_stacks:
            if s.size is not None and not skip_pixels:
                end = start + s.size
                pxoff += s.size
            else:
                curw += s.weight
                end = rstart + pxoff + int(floor(curw / totw * totalpx))
            if self.vertical:
                s.box = Rectangle(start, box.y, end - start, box.height)
            else:
                s.box = Rectangle(box.x, start, box.width, end - start)
            start = end

    def add(self, win):  # layout API
        if win.lprops.stack is not None:
            s = self.stacks.get(win.lprops.stack)
            if s is not None and not s.full:
                s.add(win)
                return True
        for s in self.auto_stacks:
            if not s.full:
                s.add(win)
                return True
        return False  # no empty stacks, reject it, so it will be floating

    def remove(self, win):  # layout API
        self.stacks[win.lprops.stack].remove(win)

    def sublayouts(self):  # layout focus API
        return self.stacks.values()

    def layout(self):
        self._assign_boxes(self.bounds)
        for s in self.stack_list:
            s.layout()

    def swap_window(self, source, target, win):
        if target.full:
            other = target.windows[0]
            target.remove(other)
            source.remove(win)
            target.add(win)
            source.add(other)
        else:
            source.remove(win)
            target.add(win)

    @stackcommand
    def cmd_up(self, stack, win):
        if self.vertical:
            stack.shift_up()
        else:
            idx = self.stack_list.index(stack)
            if idx > 0:
                self.swap_window(stack, self.stack_list[idx - 1], win)

    @stackcommand
    def cmd_down(self, stack, win):
        if self.vertical:
            stack.shift_down()
        else:
            idx = self.stack_list.index(stack)
            if idx < len(self.stacks) - 1:
                self.swap_window(stack, self.stack_list[idx + 1], win)

    @stackcommand
    def cmd_left(self, stack, win):
        if not self.vertical:
            stack.shift_up()
        else:
            idx = self.stack_list.index(stack)
            if idx > 0:
                self.swap_window(stack, self.stack_list[idx - 1], win)

    @stackcommand
    def cmd_right(self, stack, win):
        if not self.vertical:
            stack.shift_down()
        else:
            idx = self.stack_list.index(stack)
            if idx < len(self.stacks) - 1:
                self.swap_window(stack, self.stack_list[idx + 1], win)
コード例 #18
0
class Systray(Widget):

    xcore = dependency(Core, 'xcore')
    dispatcher = dependency(EventDispatcher, 'event-dispatcher')
    theme = dependency(Theme, 'theme')

    def __init__(self, *, right=False):
        super().__init__(right=right)
        self.icons = []

    def __zorro_di_done__(self):
        self.padding = self.theme.bar.box_padding
        self.spacing = self.theme.bar.icon_spacing
        self.create_window()

    def create_window(self):
        self.window = di(self).inject(
            ClientMessageWindow(
                self.xcore.create_toplevel(
                    Rectangle(0, 0, 1, 1),
                    klass=self.xcore.WindowClass.InputOnly,
                    params={}), self.systray_message))
        self.window.show()
        self.dispatcher.register_window(self.window)
        self.xcore.raw.SetSelectionOwner(
            owner=self.window.wid,
            selection=self.xcore.atom._NET_SYSTEM_TRAY_S0,
            time=0,
        )
        self.xcore.send_event('ClientMessage',
                              self.xcore.EventMask.StructureNotify,
                              self.xcore.root_window,
                              window=self.xcore.root_window,
                              type=self.xcore.atom.MANAGER,
                              format=32,
                              data=struct.pack(
                                  '<LLL',
                                  0,
                                  self.xcore.atom._NET_SYSTEM_TRAY_S0,
                                  self.window.wid,
                              ))

    def systray_message(self, msg):
        assert msg.type == self.xcore.atom._NET_SYSTEM_TRAY_OPCODE
        tm, op, wid, _, _ = struct.unpack('<LLLLL', msg.data)
        if op == 0:  # REQUEST_DOCK
            win = self.dispatcher.windows[wid]
            win.__class__ = TrayIcon
            win.systray = self
            win.reparent_to(self.bar.window)
            self.xcore.raw.ChangeWindowAttributes(
                window=win,
                params={
                    self.xcore.CW.BackPixel: self.theme.bar.background,
                    self.xcore.CW.EventMask:
                    self.xcore.EventMask.ResizeRedirect,
                })
            self.icons.append(win)
            win.show()
            self.bar.redraw.emit()
        elif op == 1:  # BEGIN_MESSAGE
            pass
        elif op == 2:  # CANCEL_MESSAGE
            pass

    def remove(self, icon):
        self.icons.remove(icon)
        self.bar.redraw.emit()

    def draw(self, canvas, l, r):
        l = int(l)
        r = int(r)
        t = self.padding.top
        h = self.bar.height - self.padding.bottom - t
        if self.right:
            r -= self.padding.right
            for i in reversed(self.icons):
                i.set_bounds(Rectangle(r - h, t, h, h))
                r -= h + self.spacing
            r += self.spacing
            r -= self.padding.left
        else:
            l += self.padding.left
            for i in self.icons:
                i.set_bounds(Rectangle(l, t, h, h))
                l += h + self.spacing
            l -= self.spacing
            l += self.padding.right
        return l, r
コード例 #19
0
ファイル: gesture.py プロジェクト: metulburr/tilenol
class Gesture(Widget):

    theme = dependency(Theme, 'theme')
    gestures = dependency(G.Gestures, 'gestures')

    def __init__(self, *, gestures=None, right=False):
        super().__init__(right=right)
        self.format = format
        self.gesture_names = gestures
        self.state = (None, None, None, None)

    def __zorro_di_done__(self):
        bar = self.theme.bar
        self.font = bar.font
        self.color = bar.text_color_pat
        self.background = bar.background_pat
        self.dig = bar.bright_color_pat
        self.inactive_color = bar.dim_color_pat
        self.padding = bar.text_padding
        self.gwidth = self.height - self.padding.top - self.padding.bottom
        if self.gesture_names is None:
            for name in self.gestures.active_gestures:
                self.gestures.add_callback(name, self._update_gesture)
        else:
            for name in self.gesture_names:
                self.gestures.add_callback(name, self._update_gesture)

    def _update_gesture(self, name, percent, state, cfg):
        px = min(int(percent * self.gwidth), self.gwidth)
        st = (name, px, state, cfg)
        if self.state != st:
            if state in {G.CANCEL, G.COMMIT}:
                self.state = (None, None, None, None)
            else:
                self.state = st
            self.bar.redraw.emit()

    def draw(self, canvas, l, r):
        name, offset, state, cfg = self.state
        if not name:
            return l, r
        fin, dir = name.split('-')
        nfin = int(fin[:-1])
        char = cfg['char']
        self.font.apply(canvas)
        if state == G.FULL:
            canvas.set_source(self.color)
        else:
            canvas.set_source(self.inactive_color)
        _, _, w, h, _, _ = canvas.text_extents(char)
        if self.right:
            x = r - self.padding.right - w
            r -= self.padding.left + self.padding.right + w
        else:
            x = l + self.padding.left
            l += self.padding.left + self.padding.right + w
        cx = x + w / 2
        cy = self.padding.top + self.gwidth / 2
        canvas.translate(cx + 1, cy)
        canvas.rotate(rotations[dir])
        canvas.translate(-cx, -cy)
        canvas.translate(0, self.gwidth / 2 - offset)
        canvas.move_to(x, self.height - self.padding.bottom)
        canvas.show_text(char)
        return l, r
コード例 #20
0
ファイル: events.py プロジェクト: metulburr/tilenol
class EventDispatcher(object):

    keys = dependency(KeyRegistry, 'key-registry')
    mouse = dependency(MouseRegistry, 'mouse-registry')
    xcore = dependency(Core, 'xcore')
    groupman = dependency(GroupManager, 'group-manager')
    screenman = dependency(ScreenManager, 'screen-manager')
    classifier = dependency(Classifier, 'classifier')
    config = dependency(Config, 'config')

    def __init__(self):
        self.windows = {}
        self.frames = {}
        self.all_windows = {}
        self.active_field = None
        self.mapping_notify = Event('mapping_notify')
        self.mapping_notify.listen(self._mapping_notify_delayed)

    def dispatch(self, ev):
        meth = getattr(self, 'handle_'+ev.__class__.__name__, None)
        if meth:
            meth(ev)
        else:
            log.warning("Unknown event ``%r''", ev)

    def register_window(self, win):
        self.all_windows[win.wid] = win

    def handle_KeyPressEvent(self, ev):
        if not self.keys.dispatch_event(ev):
            if self.active_field:
                self.active_field.handle_keypress(ev)

    def handle_KeyReleaseEvent(self, ev):
        pass  # nothing to do at the moment

    def handle_ButtonPressEvent(self, ev):
        self.mouse.dispatch_button_press(ev)

    def handle_ButtonReleaseEvent(self, ev):
        self.mouse.dispatch_button_release(ev)

    def handle_MotionNotifyEvent(self, ev):
        self.mouse.dispatch_motion(ev)

    def handle_MapRequestEvent(self, ev):
        try:
            win = self.windows[ev.window]
        except KeyError:
            log.warning("Configure request for non-existent window %r",
                ev.window)
        else:
            win.want.visible = True
            if win.frame is None:
                frm = win.create_frame()
                self.frames[frm.wid] = frm
                self.all_windows[frm.wid] = frm
            win.reparent_frame()
            if not hasattr(win, 'group'):
                self.classifier.apply(win)
                self.groupman.add_window(win)

    def handle_EnterNotifyEvent(self, ev):
        if self.mouse.drag:
            return
        try:
            win = self.frames[ev.event]
        except KeyError:
            log.warning("Enter notify for non-existent window %r", ev.event)
        else:
            if ev.mode != self.xcore.NotifyMode.Grab:
                if hasattr(win, 'pointer_enter'):
                    win.pointer_enter()
            if self.active_field:
                return
            if(win.props.get("WM_HINTS") is None
                or win.props.get('WM_HINTS')[0] & 1):
                win.focus()

    def handle_LeaveNotifyEvent(self, ev):
        if self.mouse.drag:
            return
        try:
            win = self.frames[ev.event]
        except KeyError:
            log.warning("Leave notify for non-existent window %r", ev.event)
        else:
            if ev.mode != self.xcore.NotifyMode.Grab:
                if hasattr(win, 'pointer_leave'):
                    win.pointer_leave()

    def handle_MapNotifyEvent(self, ev):
        try:
            win = self.all_windows[ev.window]
        except KeyError:
            log.warning("Map notify for non-existent window %r",
                ev.window)
        else:
            if hasattr(win, 'group') and win.group.visible:
                win.real.visible = True
                if win.frame:
                    win.frame.show()

    def handle_UnmapNotifyEvent(self, ev):
        if ev.event not in self.frames:
            return # do not need to track unmapping of unmanaged windows
        try:
            win = self.windows[ev.window]
        except KeyError:
            log.warning("Unmap notify for non-existent window %r",
                ev.window)
        else:
            win.real.visible = False
            win.done.visible = False
            if win.frame:
                win.ewmh.hiding_window(win)
                win.frame.hide()
                # According to the docs here should be reparenting of windows
                # to the root window, but that doesn't work well
            if hasattr(win, 'group'):
                win.group.remove_window(win)

    def handle_FocusInEvent(self, ev):
        if(ev.event == self.xcore.root_window
                and ev.mode not in (self.xcore.NotifyMode.Grab,
                                    self.xcore.NotifyMode.Ungrab)
                and ev.detail == getattr(self.xcore.NotifyDetail, 'None')):
            self.xcore.raw.SetInputFocus(
                focus=self.xcore.root_window,
                revert_to=self.xcore.InputFocus.PointerRoot,
                time=self.xcore.last_time,
                )
            return
        try:
            win = self.all_windows[ev.event]
        except KeyError:
            log.warning("Focus request for non-existent window %r",
                ev.event)
        else:
            if(ev.mode not in (self.xcore.NotifyMode.Grab,
                               self.xcore.NotifyMode.Ungrab)
               and ev.detail != self.xcore.NotifyDetail.Pointer):
                win.focus_in()

    def handle_FocusOutEvent(self, ev):
        try:
            win = self.all_windows[ev.event]
        except KeyError:
            log.warning("Focus request for non-existent window %r",
                ev.event)
        else:
            if(ev.mode not in (self.xcore.NotifyMode.Grab,
                               self.xcore.NotifyMode.Ungrab)
               and ev.detail != self.xcore.NotifyDetail.Pointer):
                win.focus_out()

    def handle_CreateNotifyEvent(self, ev):
        win = di(self).inject(Window.from_notify(ev))
        if win.wid in self.windows:
            log.warning("Create notify for already existent window %r",
                win.wid)
            # TODO(tailhook) clean up old window
        if win.wid in self.all_windows:
            return
        win.done.size = win.want.size
        self.xcore.raw.ChangeWindowAttributes(window=win, params={
                self.xcore.CW.EventMask: self.xcore.EventMask.PropertyChange
            })
        self.windows[win.wid] = win
        self.all_windows[win.wid] = win
        try:
            for name in self.xcore.raw.ListProperties(window=win)['atoms']:
                win.update_property(name)
        except XError:
            log.warning("Window destroyed immediately %d", win.wid)

    def handle_ConfigureNotifyEvent(self, ev):
        pass

    def handle_ReparentNotifyEvent(self, ev):
        pass

    def handle_DestroyNotifyEvent(self, ev):
        try:
            win = self.all_windows.pop(ev.window)
        except KeyError:
            log.warning("Destroy notify for non-existent window %r",
                ev.window)
        else:
            self.windows.pop(win.wid, None)
            self.frames.pop(win.wid, None)
            if hasattr(win, 'group'):
                win.group.remove_window(win)
            win.destroyed()

    def handle_ConfigureRequestEvent(self, ev):
        try:
            win = self.windows[ev.window]
        except KeyError:
            log.warning("Configure request for non-existent window %r",
                ev.window)
        else:
            win.update_size_request(ev)

    def handle_PropertyNotifyEvent(self, ev):
        try:
            win = self.windows[ev.window]
        except KeyError:
            log.warning("Property notify event for non-existent window %r",
                ev.window)
        else:
            win.update_property(ev.atom)

    def handle_ExposeEvent(self, ev):
        try:
            win = self.all_windows[ev.window]
        except KeyError:
            log.warning("Expose event for non-existent window %r",
                ev.window)
        else:
            win.expose(Rectangle(ev.x, ev.y, ev.width, ev.height))

    def handle_ClientMessageEvent(self, ev):
        type = self.xcore.atom[ev.type]
        # import struct
        # print("ClientMessage", ev, repr(type), struct.unpack('<5L', ev.data))
        win = self.all_windows[ev.window]
        if hasattr(win, 'client_message'):
            win.client_message(ev)
        else:
            log.warning("Unhandled client message %r %r %r",
                ev, type, struct.unpack('<5L', ev.data))

    def handle_ScreenChangeNotifyEvent(self, ev):
        # We only poll for events and use Xinerama for screen querying
        # because some drivers (nvidia) doesn't provide xrandr data
        # correctly
        if self.config['auto-screen-configuration']:
            if randr.check_screens(self.xcore):
                randr.configure_outputs(self.xcore,
                                        self.config['screen-dpi']/25.4)
        info = self.xcore.xinerama.QueryScreens()['screen_info']
        self.screenman.update(list(
            Rectangle(scr['x_org'], scr['y_org'],
                scr['width'], scr['height']) for scr in info))
        self.groupman.check_screens()

    def handle_NotifyEvent(self, ev):  # Xrandr events are reported here
        log.warning("Notify event %r", ev)

    def handle_MappingNotifyEvent(self, ev):
        self.mapping_notify.emit()

    def _mapping_notify_delayed(self):
        self.keys.reconfigure_keys()
コード例 #21
0
ファイル: dashboards.py プロジェクト: tailhook/fedor
class DashboardWebsock(JSONWebsockInput):

    redis = dependency(Redis, 'redis')

    @public_with_connection_id
    def subscribe(self, cid, name):
        self.output.subscribe(cid, 'dashboard:' + name)
        return 'ok'

    @public_with_connection_id
    def insert_before(self, cid, dash, hint, uri):
        data = self.redis.execute('GET', 'dashboard:{}:notepads'.format(dash))
        if data:
            data = json.loads(data.decode('utf-8'))
        else:
            data = []
        for idx, (kind, nuri) in enumerate(data):
            if nuri[1:] == hint:
                data.insert(idx, (kind, uri))
                break
        self.redis.execute('SET', 'dashboard:{}:notepads'.format(dash),
                           json.dumps(data))
        np = di(self).inject(Notepad.from_url(uri))
        self.output.subscribe(cid, np.topic)
        self.output.publish('dashboard:' + dash, [
            'dashboard.insert_before', dash, hint, {
                'ident': np.ident,
                'title': uri_to_title(uri),
                'records': np.records,
            }
        ])
        return 'ok'

    @public_with_connection_id
    def insert_after(self, cid, dash, hint, uri):
        data = self.redis.execute('GET', 'dashboard:{}:notepads'.format(dash))
        if data:
            data = json.loads(data.decode('utf-8'))
        else:
            data = []
        for idx, (kind, nuri) in enumerate(data):
            if nuri[1:] == hint:
                data.insert(idx + 1, (kind, uri))
                break
        self.redis.execute('SET', 'dashboard:{}:notepads'.format(dash),
                           json.dumps(data))
        np = di(self).inject(Notepad.from_url(uri))
        self.output.subscribe(cid, np.topic)
        self.output.publish('dashboard:' + dash, [
            'dashboard.insert_after', dash, hint, {
                'ident': np.ident,
                'title': uri_to_title(uri),
                'records': np.records,
            }
        ])
        return 'ok'

    @public_with_connection_id
    def append_left(self, cid, dash, uri):
        data = self.redis.execute('GET', 'dashboard:{}:notepads'.format(dash))
        if data:
            data = json.loads(data.decode('utf-8'))
        else:
            data = []
        data.append(('left', uri))
        self.redis.execute('SET', 'dashboard:{}:notepads'.format(dash),
                           json.dumps(data))
        np = di(self).inject(Notepad.from_url(uri))
        self.output.subscribe(cid, np.topic)
        self.output.publish('dashboard:' + dash, [
            'dashboard.append_left', dash, {
                'ident': np.ident,
                'title': uri_to_title(uri),
                'records': np.records,
            }
        ])
        return 'ok'

    @public_with_connection_id
    def append_right(self, cid, dash, uri):
        data = self.redis.execute('GET', 'dashboard:{}:notepads'.format(dash))
        if data:
            data = json.loads(data.decode('utf-8'))
        else:
            data = []
        for idx, (kind, nuri) in data:
            if nuri == hint:
                data.insert(idx + 1, (kind, uri))
                break
        self.redis.execute('SET', 'dashboard:{}:notepads'.format(dash),
                           json.dumps(data))
        data.append(('right', uri))
        np = di(self).inject(Notepad.from_url(uri))
        self.output.subscribe(cid, np.topic)
        self.output.publish('dashboard:' + dash, [
            'dashboard.append_left', dash, {
                'ident': np.ident,
                'title': uri_to_title(uri),
                'records': np.records,
            }
        ])
        return 'ok'
コード例 #22
0
ファイル: main.py プロジェクト: BlackSnak89/tilenol
class Tilenol(object):

    xcore = dependency(Core, 'xcore')
    dispatcher = dependency(EventDispatcher, 'event-dispatcher')
    config = dependency(Config, 'config')
    commander = dependency(CommandDispatcher, 'commander')

    def __init__(self, options):
        pass
        # extract options needed

    def register_gadgets(self):
        inj = di(self)
        for name, inst in self.config.gadgets():
            inj.inject(inst)
            self.commander[name] = inst

    def run(self):
        signal.signal(signal.SIGCHLD, child_handler)
        signal.signal(signal.SIGQUIT, quit_handler)

        proto = Proto()
        proto.load_xml('xproto')
        proto.load_xml('xtest')
        proto.load_xml('xinerama')
        proto.load_xml('shm')
        proto.load_xml('randr')
        self.conn = conn = Connection(proto)
        conn.connection()
        self.root_window = Root(conn.init_data['roots'][0]['root'])

        inj = DependencyInjector()
        inj['dns'] = dns.Resolver(dns.Config.system_config())
        gethub().dns_resolver = inj['dns']
        inj['xcore'] = xcore = Core(conn)
        inj['keysyms'] = keysyms = Keysyms()
        keysyms.load_default()

        cfg = inj['config'] = inj.inject(Config())
        cfg.init_extensions()

        # Hack, but this only makes GetScreenInfo work
        xcore.randr._proto.requests['GetScreenInfo'].reply.items['rates'].code\
            = compile('0', 'XPROTO', 'eval')
        if cfg['auto-screen-configuration']:
            if randr.check_screens(xcore):
                randr.configure_outputs(xcore, cfg['screen-dpi'] / 25.4)

        inj['theme'] = inj.inject(cfg.theme())
        inj['commander'] = cmd = inj.inject(CommandDispatcher())
        if hasattr(xcore, 'randr'):
            NM = xcore.randr.NotifyMask
            # We only poll for events and use Xinerama for screen querying
            # because some drivers (nvidia) doesn't provide xrandr data
            # correctly
            xcore.randr.SelectInput(window=xcore.root_window,
                                    enable=NM.ScreenChange | NM.CrtcChange
                                    | NM.OutputChange | NM.OutputProperty)

        if hasattr(xcore, 'xinerama'):
            info = xcore.xinerama.QueryScreens()['screen_info']
            screenman = inj['screen-manager'] = ScreenManager([
                Rectangle(scr['x_org'], scr['y_org'], scr['width'],
                          scr['height']) for scr in info
            ])
        else:
            screenman = inj['screen-manager'] = ScreenManager([
                Rectangle(0, 0, xcore.root['width_in_pixels'],
                          xcore.root['height_in_pixels'])
            ])
        inj.inject(screenman)

        cmd['tilenol'] = self
        keys = KeyRegistry()
        inj['key-registry'] = inj.inject(keys)
        mouse = MouseRegistry()
        inj['mouse-registry'] = inj.inject(mouse)
        inj['gestures'] = inj.inject(Gestures())

        gman = inj.inject(GroupManager(map(inj.inject, cfg.groups())))
        cmd['groups'] = gman
        inj['group-manager'] = gman

        rules = inj['classifier'] = inj.inject(Classifier())
        for cls, cond, act in cfg.rules():
            rules.add_rule(cond, act, klass=cls)

        eman = inj.inject(EventDispatcher())
        eman.all_windows[self.root_window.wid] = self.root_window
        inj['event-dispatcher'] = eman
        inj['ewmh'] = Ewmh()
        inj.inject(inj['ewmh'])

        inj.inject(self)

        cmd['env'] = EnvCommands()
        cmd['emul'] = inj.inject(EmulCommands())

        # Register hotkeys as mapping notify can be skipped on inplace restart
        keys.configure_hotkeys()

        mouse.init_buttons()
        mouse.register_buttons(self.root_window)
        self.setup_events()

        for screen_no, bar in cfg.bars():
            inj.inject(bar)
            if screen_no < len(screenman.screens):
                scr = screenman.screens[screen_no]
                if bar.position == 'bottom':
                    scr.add_bottom_bar(bar)
                else:
                    scr.add_top_bar(bar)
                bar.create_window()
                scr.updated.listen(bar.redraw.emit)

        self.register_gadgets()

        self.catch_windows()
        self.loop()

    def catch_windows(self):
        cnotify = self.xcore.proto.events['CreateNotify'].type
        mnotify = self.xcore.proto.events['MapRequest'].type
        for w in self.xcore.raw.QueryTree(window=self.root_window)['children']:
            if w == self.root_window or w in self.dispatcher.all_windows:
                continue
            try:
                attr = self.xcore.raw.GetWindowAttributes(window=w)
            except XError:  # TODO(pc) check for error code
                continue
            if attr['class'] == self.xcore.WindowClass.InputOnly:
                continue
            geom = self.xcore.raw.GetGeometry(drawable=w)
            self.dispatcher.handle_CreateNotifyEvent(
                cnotify(
                    0,
                    window=w,
                    parent=self.root_window.wid,
                    x=geom['x'],
                    y=geom['y'],
                    width=geom['width'],
                    height=geom['height'],
                    border_width=geom['border_width'],
                    override_redirect=attr['override_redirect'],
                ))
            win = self.dispatcher.windows[w]
            if (attr['map_state'] != self.xcore.MapState.Unmapped
                    and not attr['override_redirect']):
                self.dispatcher.handle_MapRequestEvent(
                    mnotify(
                        0,
                        parent=self.root_window.wid,
                        window=w,
                    ))

    def setup_events(self):
        EM = self.xcore.EventMask
        self.xcore.raw.ChangeWindowAttributes(window=self.root_window,
                                              params={
                                                  self.xcore.CW.EventMask:
                                                  EM.StructureNotify
                                                  | EM.SubstructureNotify
                                                  | EM.SubstructureRedirect
                                                  | EM.FocusChange
                                              })
        attr = self.xcore.raw.GetWindowAttributes(window=self.root_window)
        if not (attr['your_event_mask'] & EM.SubstructureRedirect):
            print("Probably another window manager is running",
                  file=sys.stderr)
            return

    def loop(self):
        for i in self.xcore.get_events():
            try:
                self.dispatcher.dispatch(i)
            except Exception:
                log.exception("Error handling event %r", i)

    def cmd_restart(self):
        inplace_restart()
コード例 #23
0
class Frame(Window):

    commander = dependency(CommandDispatcher, 'commander')
    theme = dependency(Theme, 'theme')

    def __init__(self, wid, content):
        super().__init__(wid)
        self.content = content

    def __zorro_di_done__(self):
        self.border_width = self.theme.window.border_width

    def focus(self):
        self.done.focus = True
        if self.content.lprops.floating:
            self.restack(self.xcore.StackMode.TopIf)
        self.content.focus()

    def focus_out(self):
        self.done.focus = False
        self.real.focus = False
        self.content.done.focus = False
        self.content.real.focus = False
        win = self.commander.pop('window', None)
        assert win in (self.content, None)
        self.xcore.raw.ChangeWindowAttributes(
            window=self,
            params={
                self.xcore.CW.BorderPixel: self.theme.window.inactive_border,
            },
            _ignore_error=True)

    def focus_in(self):
        self.real.focus = True
        self.content.real.focus = True
        assert self.commander.get('window') in (self.content, None)
        if not hasattr(self.content, 'group'):
            return
        self.commander['window'] = self.content
        self.commander['group'] = self.content.group
        self.commander['layout'] = self.content.group.current_layout
        self.commander['screen'] = self.content.group.screen
        self.xcore.raw.ChangeWindowAttributes(
            window=self,
            params={
                self.xcore.CW.BorderPixel: self.theme.window.active_border,
            },
            _ignore_error=True)

    def pointer_enter(self):
        self.commander['pointer_window'] = self.content

    def pointer_leave(self):
        if self.commander.get('pointer_window') == self.content:
            del self.commander['pointer_window']

    def hide(self):
        if self.commander.get('window') == self.content:
            del self.commander['window']
        super().hide()

    def destroyed(self):
        if self.commander.get('window') is self.content:
            del self.commander['window']

    def configure_content(self, rect):
        hints = self.content.want.hints
        x = 0
        y = 0
        rw = rect.width - self.border_width * 2
        rh = rect.height - self.border_width * 2
        if hints and not self.content.ignore_hints:
            width, height = self._apply_hints(rw, rh, hints)
            if width < rw:
                x = rw // 2 - width // 2
            if height < rh:
                y = rh // 2 - height // 2
            # TODO(tailhook) obey gravity
        else:
            width = rw
            height = rh
        self.content.done.size = Rectangle(x, y, width, height)
        self.xcore.raw.ConfigureWindow(window=self.content,
                                       params={
                                           self.xcore.ConfigWindow.X: x,
                                           self.xcore.ConfigWindow.Y: y,
                                           self.xcore.ConfigWindow.Width:
                                           width,
                                           self.xcore.ConfigWindow.Height:
                                           height,
                                       })

    def _apply_hints(self, width, height, hints):
        if hasattr(hints, 'width_inc'):
            incr = hints.width_inc
            base = getattr(hints, 'base_width',
                           getattr(hints, 'min_width', None))
            n = (width - base) // incr
            width = base + n * incr
        if hasattr(hints, 'max_width') and width > hints.max_width:
            width = hints.max_width
        if hasattr(hints, 'height_inc'):
            incr = hints.height_inc
            base = getattr(hints, 'base_height',
                           getattr(hints, 'min_height', None))
            n = (height - base) // incr
            height = base + n * incr
        if hasattr(hints, 'max_height') and height > hints.max_height:
            height = hints.max_height
        # TODO(tailhook) obey aspect ratio
        return width, height

    def set_bounds(self, rect, force=False):
        if not super().set_bounds(rect, force=force):
            return False
        self.configure_content(rect)
        return True

    def show(self):
        super().show()
        if self.done.size:
            self.configure_content(self.done.size)

    def add_hint(self):
        res = di(self).inject(
            HintWindow(
                self.xcore.create_window(
                    Rectangle(0, 0, 1, 1),
                    klass=self.xcore.WindowClass.InputOutput,
                    parent=self,
                    params={
                        self.xcore.CW.BackPixel:
                        self.theme.hint.background,
                        self.xcore.CW.BorderPixel:
                        self.theme.hint.border_color,
                        self.xcore.CW.OverrideRedirect:
                        True,
                        self.xcore.CW.EventMask:
                        self.xcore.EventMask.SubstructureRedirect
                        | self.xcore.EventMask.SubstructureNotify
                        | self.xcore.EventMask.EnterWindow
                        | self.xcore.EventMask.LeaveWindow
                        | self.xcore.EventMask.FocusChange
                    }), self))
        res.set_border(self.theme.hint.border_width)
        res.show()
        return res

    def toggle_border(self):
        if self.border_width == 0:
            self.set_border(self.theme.window.border_width)
        else:
            self.set_border(0)
コード例 #24
0
ファイル: groupbox.py プロジェクト: metulburr/tilenol
class Groupbox(Widget):

    theme = dependency(Theme, 'theme')

    def __init__(self, *, filled=False, first_letter=False, right=False):
        super().__init__(right=right)
        self.filled = filled
        self.first_letter = first_letter

    def __zorro_di_done__(self):
        self.state = di(self).inject(State())
        bar = self.theme.bar
        self.font = bar.font
        self.inactive_color = bar.dim_color_pat
        self.urgent_color = bar.bright_color_pat
        self.active_color = bar.text_color_pat
        self.selected_color = bar.active_border_pat
        self.subactive_color = bar.subactive_border_pat
        self.padding = bar.text_padding
        self.border_width = bar.border_width
        self.state.gman.group_changed.listen(self.bar.redraw.emit)
        Window.any_window_changed.listen(self.check_state)

    def check_state(self):
        if self.state.dirty:
            self.bar.redraw.emit()

    def draw(self, canvas, l, r):
        self.state.update()
        assert not self.right, "Sorry, right not implemented"
        self.font.apply(canvas)
        canvas.set_line_join(LINE_JOIN_ROUND)
        canvas.set_line_width(self.border_width)
        x = l
        between = self.padding.right + self.padding.left
        for gs in self.state.groups:
            gname = gs.name
            if self.first_letter:
                gname = gname[0]
            sx, sy, w, h, ax, ay = canvas.text_extents(gname)
            if gs.active:
                canvas.set_source(self.selected_color)
                if self.filled:
                    canvas.rectangle(x, 0, ax + between, self.height)
                    canvas.fill()
                else:
                    canvas.rectangle(x + 2, 2, ax + between - 4,
                                     self.height - 4)
                    canvas.stroke()
            elif gs.visible:
                canvas.set_source(self.subactive_color)
                if self.filled:
                    canvas.rectangle(x, 0, ax + between, self.height)
                    canvas.fill()
                else:
                    canvas.rectangle(x + 2, 2, ax + between - 4,
                                     self.height - 4)
                    canvas.stroke()
            if gs.urgent:
                canvas.set_source(self.urgent_color)
            elif gs.empty:
                canvas.set_source(self.inactive_color)
            else:
                canvas.set_source(self.active_color)
            canvas.move_to(x + self.padding.left,
                           self.height - self.padding.bottom)
            canvas.show_text(gname)
            x += ax + between
        return x, r
コード例 #25
0
class HintWindow(Window):

    cairo = None

    theme = dependency(Theme, 'theme')

    def __init__(self, wid, parent):
        super().__init__(wid)
        self.parent = parent
        self.redraw_ev = Event('hint.redraw')
        self.redraw_ev.listen(self.do_redraw)
        self.show_ev = Event('hint.show')
        self.show_ev.listen(self.do_show)

    def __zorro_di_done__(self):
        self.sizer = cairo.Context(
            cairo.ImageSurface(cairo.FORMAT_ARGB32, 1, 1))
        sx, sy, w, h, ax, ay = self.sizer.text_extents('1')
        self.line_height = int(h - sy)
        self.font = self.theme.hint.font
        self.font.apply(self.sizer)
        self.padding = self.theme.hint.padding
        self.color = self.theme.hint.text_color_pat
        self.background = self.theme.hint.background_pat

        self._gc = self.xcore._conn.new_xid()  # TODO(tialhook) private api?
        self.xcore.raw.CreateGC(
            cid=self._gc,
            drawable=self.xcore.root_window,
            params={},
        )

    def set_text(self, text):
        self.text = text
        lines = text.split('\n')
        w = 0
        h = 0
        for line in lines:
            sx, sy, tw, th, ax, ay = self.sizer.text_extents(line)
            w = max(w, tw)
            h += th
        w += self.padding.left + self.padding.right
        h += self.padding.top + self.padding.bottom
        w = int(w)
        h = int(h)
        need_resize = (self.cairo is None
                       or w != self.cairo.get_target().get_width()
                       and h != self.cairo.get_target().get_height)
        if need_resize:
            self.cairo = cairo.Context(
                cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h))
            self.font.apply(self.cairo)
            psz = self.parent.done.size
            self.set_bounds(
                Rectangle((psz.width - w) // 2 - self.border_width,
                          (psz.height - h) // 2 - self.border_width, w, h))
        self.redraw_ev.emit()

    def do_redraw(self):
        tgt = self.cairo.get_target()
        w = tgt.get_width()
        h = tgt.get_height()
        self.cairo.set_source(self.background)
        self.cairo.rectangle(0, 0, w, h)
        self.cairo.fill()
        self.cairo.set_source(self.color)
        y = self.padding.top + self.line_height
        for line in self.text.split('\n'):
            sx, sy, tw, th, ax, ay = self.sizer.text_extents(line)
            self.cairo.move_to((w - tw) // 2, y)
            self.cairo.show_text(line)
            y += th
        self.show_ev.emit()

    def expose(self, rect):
        self.show_ev.emit()

    def do_show(self):
        tgt = self.cairo.get_target()
        w = tgt.get_width()
        h = tgt.get_height()
        self.xcore.raw.PutImage(
            format=self.xcore.ImageFormat.ZPixmap,
            drawable=self,
            gc=self._gc,
            width=w,
            height=h,
            dst_x=0,
            dst_y=0,
            left_pad=0,
            depth=24,
            data=bytes(tgt),
        )
コード例 #26
0
class Select(GadgetBase):

    commander = dependency(CommandDispatcher, 'commander')
    dispatcher = dependency(EventDispatcher, 'event-dispatcher')

    def __init__(self, max_lines=10):
        self.window = None
        self.max_lines = max_lines
        self.redraw = Event('menu.redraw')
        self.redraw.listen(self._redraw)
        self.submit_ev = Event('menu.submit')
        self.submit_ev.listen(self._submit)
        self.complete = Event('menu.complete')
        self.complete.listen(self._complete)
        self.close = Event('menu.close')
        self.close.listen(self._close)

    def __zorro_di_done__(self):
        self.line_height = self.theme.menu.line_height

    def cmd_show(self):
        if self.window:
            self.cmd_hide()
        self._current_items = self.items()
        show_lines = min(len(self._current_items) + 1, self.max_lines)
        h = self.theme.menu.line_height
        self.height = h * self.max_lines
        bounds = self.commander['screen'].bounds._replace(height=h)
        self._img = self.xcore.pixbuf(bounds.width, h)
        wid = self.xcore.create_toplevel(
            bounds,
            klass=self.xcore.WindowClass.InputOutput,
            params={
                self.xcore.CW.BackPixel:
                self.theme.menu.background,
                self.xcore.CW.OverrideRedirect:
                True,
                self.xcore.CW.EventMask:
                self.xcore.EventMask.FocusChange
                | self.xcore.EventMask.EnterWindow
                | self.xcore.EventMask.LeaveWindow
                | self.xcore.EventMask.KeymapState
                | self.xcore.EventMask.KeyPress,
            })
        self.window = di(self).inject(
            DisplayWindow(wid, self.draw, focus_out=self._close))
        self.dispatcher.all_windows[wid] = self.window
        self.dispatcher.frames[wid] = self.window  # dirty hack
        self.window.show()
        self.window.focus()
        self.text_field = di(self).inject(
            TextField(self.theme.menu,
                      events={
                          'draw': self.redraw,
                          'submit': self.submit_ev,
                          'complete': self.complete,
                          'close': self.close,
                      }))
        self.dispatcher.active_field = self.text_field
        self._redraw()

    def cmd_hide(self):
        self._close()

    def draw(self, rect=None):
        self._img.draw(self.window)

    def match_lines(self, value):
        matched = set()
        for line, res in self._current_items:
            if line in matched: continue
            if line.startswith(value):
                matched.add(line)
                yield (line, [(1, line[:len(value)]),
                              (0, line[len(value):])], res)
        ncval = value.lower()
        for line, res in self._current_items:
            if line in matched: continue
            if line.lower().startswith(value):
                matched.add(line)
                yield (line, [(1, line[:len(value)]),
                              (0, line[len(value):])], res)
        for line, res in self._current_items:
            if line in matched: continue
            if ncval in line.lower():
                matched.add(line)
                opcodes = []
                for pt in re.compile('((?i)' + re.escape(value) +
                                     ')').split(line):
                    opcodes.append((pt.lower() == ncval, pt))
                yield (line, opcodes, res)

    def _redraw(self):
        if not self.window and not self.text_field:
            return
        lines = list(
            islice(self.match_lines(self.text_field.value), self.max_lines))
        newh = (len(lines) + 1) * self.line_height
        if newh != self.height:
            # don't need to render, need resize
            self.height = newh
            bounds = self.commander['screen'].bounds._replace(height=newh)
            self._img = self.xcore.pixbuf(bounds.width, newh)
            self.window.set_bounds(bounds)
        ctx = self._img.context()
        ctx.set_source(self.theme.menu.background_pat)
        ctx.rectangle(0, 0, self._img.width, self._img.height)
        ctx.fill()
        sx, sy, _, _, ax, ay = ctx.text_extents(self.text_field.value)
        self.text_field.draw(ctx)
        th = self.theme.menu
        pad = th.padding
        y = self.line_height
        for text, opcodes, value in lines:
            ctx.move_to(pad.left, y + self.line_height - pad.bottom)
            for op, tx in opcodes:
                ctx.set_source(th.highlight_pat if op else th.text_pat)
                ctx.show_text(tx)
            y += self.line_height
        self.draw()

    def _submit(self):
        input = self.text_field.value
        matched = None
        value = None
        for matched, opcodes, value in self.match_lines(input):
            break
        self.submit(input, matched, value)
        self._close()

    def _close(self):
        if self.window:
            self.window.destroy()
            self.window = None
        if self.dispatcher.active_field == self.text_field:
            self.dispatcher.active_field = None
        self.text_field = None

    def _complete(self):
        text, _, val = next(iter(self.match_lines(self.text_field.value)))
        self.text_field.value = text
        self.text_field.sel_start = len(text)
        self.text_field.sel_width = 0
        self.redraw.emit()
コード例 #27
0
class KeyRegistry(object):

    keysyms = dependency(Keysyms, 'keysyms')
    xcore = dependency(Core, 'xcore')
    config = dependency(Config, 'config')
    commander = dependency(CommandDispatcher, 'commander')

    def __init__(self):
        self.keys = {}

    def configure_hotkeys(self):
        self.xcore.init_keymap()
        for key, cmd in self.config.keys():
            self.add_key(key, self.commander.callback(*cmd))
        self.register_keys(self.xcore.root_window)

    def reconfigure_keys(self):
        if self.keys:
            self.unregister_keys(self.xcore.root_window)
        self.keys = {}
        self.configure_hotkeys()

    def parse_key(self, keystr):
        mod = 0
        if keystr[0] == '<':
            keystr = keystr[1:-1]
            if '-' in keystr:
                mstr, sym = keystr.split('-')
                if 'S' in mstr:
                    mod |= self.xcore.ModMask.Shift
                if 'C' in mstr:
                    mod |= self.xcore.ModMask.Control
                if 'W' in mstr:
                    mod |= getattr(self.xcore.ModMask, '4')
            else:
                sym = keystr
        else:
            sym = keystr
            if sym.lower() != sym:
                mod = self.xcore.ModMask.Shift
            sym = sym.lower()
        code = self.keysyms.name_to_code[sym]
        return mod, code

    def add_key(self, keystr, handler):
        m = hotkey_re.match(keystr)
        if not m:
            raise ValueError(keystr)
        try:
            modmask, keysym = self.parse_key(m.group(0))
        except (KeyError, ValueError):
            log.error("Can't parse key %r", m.group(0))
        else:
            self.keys[modmask, keysym] = handler

    def init_modifiers(self):
        # TODO(tailhook) probably calculate them instead of hardcoding
        caps = self.xcore.ModMask.Lock  # caps lock
        num = getattr(self.xcore.ModMask, '2')  # mod2 is usually numlock
        mode = getattr(self.xcore.ModMask, '5')  # mod5 is usually mode_switch
        self.extra_modifiers = [
            0,
            caps,
            num,
            mode,
            caps | num,
            num | mode,
            caps | num | mode,
        ]
        self.modifiers_mask = ~(caps | num | mode)

    def register_keys(self, win):
        self.init_modifiers()
        for mod, key in self.keys:
            kcodes = self.xcore.keysym_to_keycode[key]
            if not kcodes:
                log.error("No mapping for key %r",
                          self.keysyms.code_to_name[key])
                continue
            for kcode in kcodes:
                for extra in self.extra_modifiers:
                    self.xcore.raw.GrabKey(
                        owner_events=False,
                        grab_window=win,
                        modifiers=mod | extra,
                        key=kcode,
                        keyboard_mode=self.xcore.GrabMode.Async,
                        pointer_mode=self.xcore.GrabMode.Async,
                    )

    def unregister_keys(self, win):
        self.xcore.raw.UngrabKey(
            grab_window=win,
            modifiers=self.xcore.ModMask.Any,
            key=self.xcore.Grab.Any,
        )

    def dispatch_event(self, event):
        try:
            kcode = self.xcore.keycode_to_keysym[event.detail]
            handler = self.keys[event.state & self.modifiers_mask, kcode]
        except KeyError:
            return False
        else:
            try:
                handler()
            except Exception as e:
                log.exception("Error handling keypress %r",
                              event,
                              exc_info=(type(e), e, e.__traceback__))
コード例 #28
0
ファイル: yahoo_weather.py プロジェクト: metulburr/tilenol
class YahooWeather(Widget):

    tags_to_fetch = (
        'location',
        'units',
        'wind',
        'atmosphere',
        'astronomy',
        'condition',
    )

    theme = dependency(Theme, 'theme')

    def __init__(self,
                 location,
                 *,
                 picture_url=DEFAULT_PIC,
                 format='{condition_temp}°{units_temperature}',
                 metric=True,
                 right=False):
        """
        Location should be either a woeid or a name string.

        Change metric to False for imperial units.

        Set `picture_url` to None to hide picture.

        Available format variables:
            astronomy_sunrise, astronomy_sunset
            atmosphere_humidity, atmosphere_visibility,
            atmosphere_pressure, atmosphere_rising
            condition_text, condition_code, condition_temp, condition_date
            location_city. location_region, location_country
            units_temperature, units_distance, units_pressure, units_speed
            wind_chill, wind_direction, wind_speed
        """
        super().__init__(right=right)
        self.location = location
        self.format = format
        self.metric = metric
        self.picture_url = picture_url

        self.text = '--'
        self.image = None
        self.oldimg_url = None

    def __zorro_di_done__(self):
        bar = self.theme.bar
        self.font = bar.font
        self.color = bar.text_color_pat
        self.padding = bar.text_padding
        gethub().do_spawnhelper(self._update_handler)

    def _update_handler(self):
        try:
            woeid = int(self.location)
        except ValueError:
            woeid = self.fetch_woeid()
        self.uri = "{0}{1}".format(
            WEATHER_URL,
            urlencode({
                'w': woeid,
                'u': self.metric and 'c' or 'f'
            }))
        while True:
            result = self.fetch()
            if result is not None:
                self.text = self.format.format_map(result)
                if self.picture_url is not None:
                    self.fetch_image(result)
            sleep(600)

    def fetch_image(self, data):
        img_url = None
        try:
            img_url = self.picture_url.format_map(data)
            if img_url == self.oldimg_url and self.image:
                return
            data = fetchurl(img_url)
            self.image = cairo.ImageSurface.create_from_png(BytesIO(data))
            self.oldimg_url = img_url
        except Exception as e:
            log.exception("Error fetching picture %r", img_url, exc_info=e)
            self.image = None

    def fetch_woeid(self):
        woeid = None
        while woeid is None:
            try:
                data = fetchurl(QUERY_URL,
                                query={
                                    'q':
                                    "select woeid from geo.places "
                                    "where text='{0}'".format(self.location),
                                    'format':
                                    'json'
                                })
                data = json.loads(data.decode('ascii'))['query']
                if data['count'] > 1:
                    woeid = data['results']['place'][0]['woeid']
                else:
                    woeid = data['results']['place']['woeid']
            except Exception as e:
                log.exception("Error fetching woeid", exc_info=e)
                sleep(60)
            else:
                if woeid is None:
                    sleep(60)
        return woeid

    def fetch(self):
        try:
            data = fetchurl(self.uri)
            xml = ET.fromstring(data.decode('ascii'))
        except Exception as e:
            log.exception("Error fetching weather info", exc_info=e)
            return None
        data = dict()
        for tag in self.tags_to_fetch:
            elem = xml.find('.//{%s}%s' % (WEATHER_NS, tag))
            for attr, val in elem.attrib.items():
                data['{0}_{1}'.format(tag, attr)] = val
        return data

    def draw(self, canvas, l, r):
        self.font.apply(canvas)
        _, _, w, h, _, _ = canvas.text_extents(self.text)
        if self.image:
            iw = self.image.get_width()
            ih = self.image.get_height()
            imh = self.height
            imw = int(iw / ih * imh + 0.5)
            scale = ih / imh
            if self.right:
                x = r - w - self.padding.right - imw
            else:
                x = l
            y = 0
            pat = cairo.SurfacePattern(self.image)
            pat.set_matrix(
                cairo.Matrix(xx=scale, yy=scale, x0=-x * scale, y0=-y * scale))
            pat.set_filter(cairo.FILTER_BEST)
            canvas.set_source(pat)
            canvas.rectangle(x, 0, imw, imh)
            canvas.fill()
        else:
            imw = 0
        canvas.set_source(self.color)
        if self.right:
            x = r - self.padding.right - w
            r -= self.padding.left + self.padding.right + w + imw
        else:
            x = l + self.padding.left
            l += self.padding.left + self.padding.right + w + imw
        canvas.move_to(x, self.height - self.padding.bottom)
        canvas.show_text(self.text)
        return l, r
コード例 #29
0
class Bar(object):

    xcore = dependency(Core, 'xcore')
    dispatcher = dependency(EventDispatcher, 'event-dispatcher')
    theme = dependency(Theme, 'theme')

    def __init__(self, widgets, position='top'):
        self.widgets = widgets
        self.position = position
        self.bounds = None
        self.window = None
        self.redraw = Event('bar.redraw')
        self.redraw.listen(self.expose)

    def __zorro_di_done__(self):
        bar = self.theme.bar
        self.height = bar.height
        self.background = bar.background_pat
        inj = di(self).clone()
        inj['bar'] = self
        for w in self.widgets:
            w.height = self.height
            inj.inject(w)

    def set_bounds(self, rect):
        self.bounds = rect
        self.width = rect.width
        stride = self.xcore.bitmap_stride
        self.img = self.xcore.pixbuf(self.width, self.height)
        self.cairo = self.img.context()
        if self.window and not self.window.set_bounds(rect):
            self.redraw.emit()

    def create_window(self):
        EM = self.xcore.EventMask
        CW = self.xcore.CW
        self.window = DisplayWindow(
            self.xcore.create_toplevel(
                self.bounds,
                klass=self.xcore.WindowClass.InputOutput,
                params={
                    CW.EventMask: EM.Exposure | EM.SubstructureNotify,
                    CW.OverrideRedirect: True,
                }), self.expose)
        self.window.want.size = self.bounds
        di(self).inject(self.window)
        self.dispatcher.register_window(self.window)
        self.window.show()

    def expose(self, rect=None):
        # TODO(tailhook) set clip region to specified rectangle
        self.cairo.set_source(self.background)
        self.cairo.rectangle(0, 0, self.width, self.height)
        self.cairo.fill()
        l = 0
        r = self.width
        for i in self.widgets:
            self.cairo.save()
            self.cairo.rectangle(l, 0, r - l, self.height)
            self.cairo.clip()
            l, r = i.draw(self.cairo, l, r)
            self.cairo.restore()
        self.img.draw(self.window)
コード例 #30
0
class Window(BaseWindow):

    xcore = dependency(Core, 'xcore')
    ewmh = dependency(Ewmh, 'ewmh')
    theme = dependency(Theme, 'theme')

    border_width = 0
    ignore_hints = False
    any_window_changed = Event('Window.any_window_changed')

    def __init__(self, wid):
        super().__init__(wid)
        self.frame = None
        self.want = State()
        self.done = State()
        self.real = State()
        self.props = {}
        self.lprops = LayoutProperties(self)
        self.property_changed = Event('window.property_changed')
        self.protocols = set()
        self.ignore_protocols = set()

    def __repr__(self):
        return '<{} {}>'.format(self.__class__.__name__, self.wid)

    @classmethod
    def from_notify(cls, notify):
        win = cls(notify.window)
        win.parent = notify.parent
        win.override = notify.override_redirect
        win.want.size = SizeRequest.from_notify(notify)
        return win

    def update_size_request(self, req):
        msk = self.xcore.ConfigWindow
        sr = self.want.size
        if req.value_mask & msk.X:
            sr.x = req.x
        if req.value_mask & msk.Y:
            sr.y = req.y
        if req.value_mask & msk.Width:
            sr.width = req.width
        if req.value_mask & msk.Height:
            sr.height = req.height
        sz = self.done.size
        if sz is None:
            sz = sr
        self.send_event(
            'ConfigureNotify',
            window=self,
            event=self,
            x=sz.x,
            y=sz.y,
            width=sz.width,
            height=sz.height,
            border_width=0,
            above_sibling=0,
            override_redirect=False,
        )

    def send_event(self, event_type, event_mask=0, **kw):
        self.xcore.send_event(event_type, event_mask, self, **kw)

    def __index__(self):
        return self.wid

    def show(self):
        if self.done.visible:
            return False
        self.done.visible = True
        self.any_window_changed.emit()
        self.xcore.raw.MapWindow(window=self)
        if self.frame:
            self.ewmh.showing_window(self)
            self.frame.show()
        return True

    def hide(self):
        if not self.done.visible:
            return False
        self.done.visible = False
        self.any_window_changed.emit()
        if self.frame:
            self.ewmh.hiding_window(self)
            self.frame.hide()
        else:
            self.xcore.raw.UnmapWindow(window=self)
        return True

    def set_bounds(self, rect, force=False):
        if not force and self.done.size == rect:
            return False
        self.any_window_changed.emit()
        if self.frame:
            self.frame.set_bounds(rect)
        else:
            self.done.size = rect
            self.xcore.raw.ConfigureWindow(
                window=self,
                params={
                    self.xcore.ConfigWindow.X:
                    rect.x & 0xFFFF,
                    self.xcore.ConfigWindow.Y:
                    rect.y & 0xFFFF,
                    self.xcore.ConfigWindow.Width:
                    rect.width - self.border_width * 2,
                    self.xcore.ConfigWindow.Height:
                    rect.height - self.border_width * 2,
                })
        return True

    @property
    def toplevel(self):
        return self.parent == self.xcore.root_window

    def reparent_to(self, window):
        self.xcore.raw.ChangeSaveSet(window=self,
                                     mode=self.xcore.SetMode.Insert)
        self.xcore.raw.ReparentWindow(window=self, parent=window, x=0, y=0)

    def reparent_frame(self):
        self.reparent_to(self.frame)

    def reparent_root(self):
        self.xcore.raw.ReparentWindow(window=self,
                                      parent=self.xcore.root_window,
                                      x=0,
                                      y=0,
                                      _ignore_error=True)  # already destroyed
        self.xcore.raw.ChangeSaveSet(window=self,
                                     mode=self.xcore.SetMode.Delete,
                                     _ignore_error=True)  # already destroyed

    def create_frame(self):
        s = self.want.size
        if self.lprops.floating:
            border_width = self.theme.window.border_width
            s.x = s.x - border_width
            s.y = s.y - border_width
        self.frame = di(self).inject(
            Frame(
                self.xcore.create_toplevel(
                    s,
                    klass=self.xcore.WindowClass.InputOutput,
                    params={
                        self.xcore.CW.BackPixel:
                        self.theme.window.background,
                        self.xcore.CW.BorderPixel:
                        self.theme.window.inactive_border,
                        self.xcore.CW.EventMask:
                        self.xcore.EventMask.SubstructureRedirect
                        | self.xcore.EventMask.SubstructureNotify
                        | self.xcore.EventMask.EnterWindow
                        | self.xcore.EventMask.LeaveWindow
                        | self.xcore.EventMask.FocusChange
                    }), self))
        self.frame.want.size = s
        self.frame.done.size = s
        self.frame.set_border(self.frame.border_width)
        return self.frame

    def update_property(self, atom):
        try:
            self._set_property(self.xcore.atom[atom].name,
                               *self.xcore.get_property(self, atom))
        except XError:
            log.debug("Error getting property for window %r", self)

    def focus(self):
        self.done.focus = True
        want_input = True
        if "WM_TAKE_FOCUS" in self.protocols:
            self.send_event(
                'ClientMessage',
                window=self,
                type=self.xcore.atom.WM_PROTOCOLS,
                format=32,
                data=struct.pack('<LL', self.xcore.atom.WM_TAKE_FOCUS,
                                 self.xcore.last_time),
            )
            want_input = is_window_needs_input(self)
        if want_input:
            self.xcore.raw.SetInputFocus(
                focus=self,
                revert_to=self.xcore.InputFocus.PointerRoot,
                time=self.xcore.last_time,
            )

    def _set_property(self, name, typ, value):
        if name == 'WM_NORMAL_HINTS':
            self.want.hints = SizeHints.from_property(typ, value)
        if name == 'WM_PROTOCOLS':
            self.protocols = set(self.xcore.atom[p].name for p in value
                                 if p not in self.ignore_protocols)
        if name in self.lprops.long_to_short:
            if isinstance(value, tuple) and len(value) == 1:
                value = value[0]
            super(LayoutProperties,
                  self.lprops).__setattr__(self.lprops.long_to_short[name],
                                           value)
        elif name.startswith('_TN_LP_'):
            if isinstance(value, tuple) and len(value) == 1:
                value = value[0]
            super(LayoutProperties,
                  self.lprops).__setattr__(name[len('_TN_LP_'):].lower(),
                                           value)
        elif name == '_NET_WM_ICON':
            icons = self.icons = []
            lst = list(value)

            def cvt(px):
                a = px >> 24
                k = a / 255.0
                r = (px >> 16) & 0xff
                g = (px >> 8) & 0xff
                b = px & 0xff
                return (a << 24) | (int(r * k) << 16) | (
                    int(g * k) << 8) | int(b * k)

            while lst:
                w = lst.pop(0)
                h = lst.pop(0)
                idata = [cvt(px) for px in lst[:w * h]]
                del lst[:w * h]
                icons.append((w, h, idata))
            icons.sort()
        self.props[name] = value
        self.property_changed.emit()
        self.any_window_changed.emit()

    def draw_icon(self, canvas, x, y, size):
        for iw, ih, data in self.icons:
            if iw >= size or ih >= size:
                break
        scale = min(iw / size, ih / size)
        data = array.array('I', data)
        assert data.itemsize == 4
        surf = cairo.ImageSurface.create_for_data(memoryview(data),
                                                  cairo.FORMAT_ARGB32, iw, ih,
                                                  iw * 4)
        pat = cairo.SurfacePattern(surf)
        pat.set_matrix(
            cairo.Matrix(xx=scale, yy=scale, x0=-x * scale, y0=-y * scale))
        pat.set_filter(cairo.FILTER_BEST)
        canvas.set_source(pat)
        canvas.rectangle(x, y, size, size)
        canvas.fill()

    def set_property(self, name, value):
        if isinstance(value, int):
            self.xcore.raw.ChangeProperty(window=self,
                                          mode=self.xcore.PropMode.Replace,
                                          property=getattr(
                                              self.xcore.atom, name),
                                          type=self.xcore.atom.CARDINAL,
                                          format=32,
                                          data_len=1,
                                          data=struct.pack('<L', value))
        elif isinstance(value, Window):
            self.xcore.raw.ChangeProperty(window=self,
                                          mode=self.xcore.PropMode.Replace,
                                          property=getattr(
                                              self.xcore.atom, name),
                                          type=self.xcore.atom.WINDOW,
                                          format=32,
                                          data_len=1,
                                          data=struct.pack('<L', value))
        elif isinstance(value, (str, bytes)):
            if isinstance(value, str):
                value = value.encode('utf-8')
            self.xcore.raw.ChangeProperty(window=self,
                                          mode=self.xcore.PropMode.Replace,
                                          property=getattr(
                                              self.xcore.atom, name),
                                          type=self.xcore.atom.UTF8_STRING,
                                          format=8,
                                          data=value)
        elif (isinstance(value, tuple)
              and all(isinstance(i, Atom) for i in value)):
            self.xcore.raw.ChangeProperty(
                window=self,
                mode=self.xcore.PropMode.Replace,
                property=getattr(self.xcore.atom, name),
                type=self.xcore.atom.ATOM,
                format=32,
                data_len=len(value),
                data=struct.pack('<{}L'.format(len(value)), *value))
        elif value is None:
            self.xcore.raw.DeleteProperty(window=self,
                                          property=getattr(
                                              self.xcore.atom, name))
        else:
            raise NotImplementedError(value)

    def destroyed(self):
        if self.frame:
            return self.frame.destroy()

    def destroy(self):
        self.xcore.raw.DestroyWindow(window=self)

    def cmd_close(self):
        delw = self.xcore.atom.WM_DELETE_WINDOW
        if delw in self.props.get('WM_PROTOCOLS', ()):
            self.send_event(
                'ClientMessage',
                window=self,
                type=self.xcore.atom.WM_PROTOCOLS,
                format=32,
                data=struct.pack('<LL', delw, 0),
            )
        else:
            log.warning("Can't close window gracefully, you can kill it")

    def cmd_kill(self):
        self.xcore.raw.KillClient(resource=self)

    def make_floating(self):
        if self.lprops.floating:
            return
        gr = self.group
        gr.remove_window(self)
        self.lprops.floating = True
        gr.add_window(self)

    cmd_make_floating = make_floating

    def make_tiled(self):
        if not self.lprops.floating:
            return
        gr = self.group
        gr.remove_window(self)
        self.lprops.floating = False
        gr.add_window(self)

    cmd_make_tiled = make_tiled

    def restack(self, stack_mode):
        self.stack_mode = stack_mode
        self.xcore.raw.ConfigureWindow(window=self,
                                       params={
                                           self.xcore.ConfigWindow.StackMode:
                                           stack_mode,
                                       })

    def set_border(self, width):
        self.border_width = width
        self.xcore.raw.ConfigureWindow(window=self,
                                       params={
                                           self.xcore.ConfigWindow.BorderWidth:
                                           width,
                                       })
        if self.done.size:
            self.set_bounds(self.done.size, force=True)

    def cmd_toggle_border(self):
        if self.frame:
            self.frame.toggle_border()

    def client_message(self, ev):
        if ev.type == self.xcore.atom._NET_WM_STATE:
            op, p1, p2, src, _ = struct.unpack('<5L', ev.data)
            if op is 1:  # _NET_WM_STATE_ADD
                atom = self.xcore.atom._NET_WM_STATE_FULLSCREEN
                if atom == p1:
                    self.set_property("_NET_WM_STATE", (atom, ))
            elif op is 0:  # _NET_WM_STATE_REMOVE
                atom = self.xcore.atom._NET_WM_STATE_FULLSCREEN
                if atom == p1:
                    self.set_property("_NET_WM_STATE", None)
            elif op is 2:  # _NET_WM_STATE_TOGGLE
                pass  # TODO(tailhook) implement me