From 93a633fb173cea10d49660d92b6ad79fa82ef8c8 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Sat, 26 Dec 2015 12:18:51 +1100 Subject: [PATCH] Add pygtk display. This is an alternate display, using pygtk. It also provides an alternate event loop, and the alternate loop must be used. This is wart which will be resolved eventually. Only a constant-width font is used. Obviously that must change. Signed-off-by: NeilBrown --- edlib.c | 25 ++++- python/display-pygtk.py | 240 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 259 insertions(+), 6 deletions(-) create mode 100644 python/display-pygtk.py diff --git a/edlib.c b/edlib.c index f8fdcb4f..a5b52835 100644 --- a/edlib.c +++ b/edlib.c @@ -57,6 +57,10 @@ int main(int argc, char *argv[]) struct call_return cr; struct editor *ed; struct pane *vroot = editor_new(); + int gtk = 0; + + if (argc > 1 && strcmp(argv[1], "-g") == 0) + gtk = 1; ed = pane2ed(vroot); setlocale(LC_ALL, ""); @@ -64,17 +68,27 @@ int main(int argc, char *argv[]) editor_load_module(ed, "lib-line-count"); editor_load_module(ed, "lib-search"); - editor_load_module(ed, "lib-libevent"); - editor_load_module(ed, "display-ncurses"); - call3("libevent:activate", vroot, 0, NULL); + editor_load_module(ed, "lang-python"); + if (gtk) { + call5("python-load", vroot, 0, NULL, "python/display-pygtk.py", 0); + call3("pygtkevent:activate", vroot, 0, NULL); + ci.key = "display-pygtk"; + } else { + editor_load_module(ed, "lib-libevent"); + editor_load_module(ed, "display-ncurses"); + call3("libevent:activate", vroot, 0, NULL); + ci.key = "display-ncurses"; + } ci.home = ci.focus = vroot; - ci.key = "display-ncurses"; cr.c = take_pane; cr.p = NULL; ci.comm2 = &cr.c; if (key_handle(&ci) <= 0) exit(1); - root = cr.p; + if (gtk) + root = pane_attach(cr.p, "input", NULL, NULL); + else + root = cr.p; global = pane_attach(root, "messageline", NULL, NULL); global = pane_attach(global, "global-keymap", NULL, NULL); @@ -85,7 +99,6 @@ int main(int argc, char *argv[]) if (b) p = doc_from_text(b, "*Welcome*", WelcomeText); if (p) { - editor_load_module(ed, "lang-python"); memset(&ci, 0, sizeof(ci)); ci.home = ci.focus = p; ci.key = "python-load"; diff --git a/python/display-pygtk.py b/python/display-pygtk.py new file mode 100644 index 00000000..b317b2eb --- /dev/null +++ b/python/display-pygtk.py @@ -0,0 +1,240 @@ +# -*- coding: utf-8 -*- +# Copyright Neil Brown ©2015 +# May be distributed under terms of GPLv2 - see file:COPYING + +# edlib display module using pygtk to create a window, handle drawing, and +# receive mouse/keyboard events. +# provides eventloop function using gtk.main. + +import sys +import os +import pygtk +import gtk +import pango +import thread +import gobject +import glib + +class EdDisplay(gtk.Window): + def __init__(self, home): + gtk.Window.__init__(self) + self.pane = edlib.Pane(home, self.handle, None) + self.pane.w = 80 + self.pane.h = 24 + self.panes = {} + self.set_default_size(700, 300) + self.set_title("EDLIB") + self.connect('destroy', self.close_win) + self.create_ui() + self.show() + + def handle(self, key, **a): + if key == "pane-text": + (x,y) = a["xy"] + c = a["extra"] + f = a["focus"] + if "str2" in a: + attr = a["str2"] + else: + attr = "" + pm = self.get_pixmap(f); + self.draw_char(pm, x, y, c, attr) + return True + if key == "pane-clear": + f = a["focus"] + pm = self.get_pixmap(f) + self.do_clear(pm) + return True + if key == "Notify:Close": + f = a["focus"] + if f and f in self.panes: + del self.panes[f] + return True + + return None + + def get_pixmap(self, p): + if p in self.panes: + pm = self.panes[p] + (w,h) = pm.get_size() + if w == p.w * self.charwidth and h == p.h * self.lineheight: + return pm + del self.panes[p] + else: + self.pane.add_notify(p, "Notify:Close") + + self.panes[p] = gtk.gdk.Pixmap(self.window, p.w * self.charwidth, p.h * self.lineheight) + return self.panes[p] + + def close_win(self, *a): + self.destroy() + + def create_ui(self): + text = gtk.DrawingArea() + self.text = text + self.add(text) + text.show() + self.fd = pango.FontDescription("mono 12") + text.modify_font(self.fd) + ctx = text.get_pango_context() + metric = ctx.get_metrics(self.fd) + self.lineheight = (metric.get_ascent() + metric.get_descent()) / pango.SCALE + self.charwidth = metric.get_approximate_char_width() / pango.SCALE + self.set_default_size(self.charwidth * 80, self.lineheight * 24) + self.gc = None + self.bg = None + + self.text.connect("expose-event", self.refresh) + self.text.connect("button-press-event", self.press) + self.text.connect("key-press-event", self.keystroke) + self.text.connect("configure-event", self.reconfigure) + self.text.set_events(gtk.gdk.EXPOSURE_MASK| + gtk.gdk.STRUCTURE_MASK| + gtk.gdk.BUTTON_PRESS_MASK| + gtk.gdk.BUTTON_RELEASE_MASK| + gtk.gdk.KEY_PRESS_MASK| + gtk.gdk.KEY_RELEASE_MASK); + self.text.set_property("can-focus", True) + + + def refresh(self, *a): + self.pane.refresh() + l = self.panes.keys() + l.sort(key=lambda pane: pane.abs_z) + for p in l: + pm = self.panes[p] + (rx,ry) = p.abs(0,0) + self.text.window.draw_drawable(self.bg, pm, 0, 0, + rx * self.charwidth, ry * self.lineheight, + -1, -1) + + def reconfigure(self, w, ev): + alloc = w.get_allocation() + rows = int(alloc.height / self.lineheight) + cols = int(alloc.width / self.charwidth) + self.pane.w = cols + self.pane.h = rows + self.text.queue_draw() + + def press(self, c, event): + c.grab_focus() + x = int(event.x / self.charwidth) + y = int(event.y / self.lineheight) + s = "Click-" + ("%d"%event.button) + if event.state & gtk.gdk.SHIFT_MASK: + s = "S-" + s; + if event.state & gtk.gdk.CONTROL_MASK: + s = "C-" + s; + if event.state & gtk.gdk.MOD1_MASK: + s = "M-" + s; + self.pane.call_xy("Mouse-event", "Click-1", self.pane, (x,y)) + self.refresh() + + eventmap = { "Return" : "Return", + "Tab" : "Tab", + "Escape" : "ESC", + "Linefeed" : "LF", + "Down" : "Down", + "Up" : "Up", + "Left" : "Left", + "Right" : "Right", + "Home" : "Home", + "End" : "End", + "BackSpace" : "Backspace", + "Delete" : "Del", + "Insert" : "Ins", + "Page_Up" : "Prior", + "Page_Down" : "Next", + } + def keystroke(self, c, event): + kv = gtk.gdk.keyval_name(event.keyval) + if kv in self.eventmap: + s = self.eventmap[kv] + if event.state & gtk.gdk.SHIFT_MASK: + s = "S-" + s; + if event.state & gtk.gdk.CONTROL_MASK: + s = "C-" + s; + else: + s = event.string + if len(s) == 0: + return + if ord(s[0]) < 32: + s = "C-Chr-" + chr(ord(s[0])+64) + else: + s = "Chr-" + s + if event.state & gtk.gdk.MOD1_MASK: + s = "M-" + s; + self.pane.call_focus("Keystroke", self.pane, s) + self.pane.refresh() + + def draw_char(self, pm, x, y, c, attr): + t = self.text + if not self.gc: + self.gc = t.window.new_gc() + cmap = t.get_colormap() + self.gc.set_foreground(cmap.alloc_color(gtk.gdk.color_parse("blue"))) + if not self.bg: + self.bg = t.window.new_gc() + cmap = t.get_colormap() + self.bg.set_foreground(cmap.alloc_color(gtk.gdk.color_parse("lightyellow"))) + layout = t.create_pango_layout(unichr(c)) + bg = self.bg; fg = self.gc + if "inverse" in attr: + fg,bg = bg,fg + pm.draw_rectangle(bg, True, x * self.charwidth, y * self.lineheight, self.charwidth, self.lineheight) + pm.draw_layout(fg, x * self.charwidth, y * self.lineheight, layout) + t.queue_draw() + return False + + def do_clear(self, pm): + + t = self.text + if not self.bg: + self.bg = t.window.new_gc() + cmap = t.get_colormap() + self.bg.set_foreground(cmap.alloc_color(gtk.gdk.color_parse("lightyellow"))) + (w,h) = pm.get_size() + pm.draw_rectangle(self.bg, True, 0, 0, w, h) + +def new_display(key, home, comm2, **a): + disp = EdDisplay(home) + comm2('callback', disp.pane) + return 1 + +class events: + def __init__(self): + self.active = True + + def read(self, key, focus, comm2, numeric, **a): + gobject.io_add_watch(numeric, gobject.IO_IN, self.docall, comm2, focus, numeric) + + def docall(self, comm, focus, fd): + comm2.call("callback", focus, fd) + return 1 + + def signal(self, key, focus, comm2, numeric, **a): + pass + + def run(self, key, **a): + if self.active: + gtk.main() + return 1 + else: + return 0 + + def deactivate(self, key, **a): + self.active = False + gtk.main_quit() + return 1 + +def events_activate(key, home, focus, **a): + ev = events() + home.call("global-set-command", focus, "event:read", ev.read) + home.call("global-set-command", focus, "event:signal", ev.signal) + home.call("global-set-command", focus, "event:run", ev.run) + home.call("global-set-command", focus, "event:deactivate", ev.deactivate) + + return 1 + +editor.call("global-set-command", pane, "display-pygtk", new_display); +editor.call("global-set-command", pane, "pygtkevent:activate", events_activate); -- 2.39.5