diff --git a/app/browser/buffer.py b/app/browser/buffer.py index ff5f356..50317d3 100644 --- a/app/browser/buffer.py +++ b/app/browser/buffer.py @@ -2,20 +2,20 @@ # -*- coding: utf-8 -*- # Copyright (C) 2018 Andy Stewart -# +# # Author: Andy Stewart # Maintainer: Andy Stewart -# +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . @@ -27,15 +27,15 @@ from buffer import Buffer class BrowserBuffer(Buffer): def __init__(self, buffer_id, url, width, height): Buffer.__init__(self, buffer_id, url, width, height, QColor(255, 255, 255, 255)) - + self.buffer_widget = QWebView() self.buffer_widget.resize(self.width, self.height) self.buffer_widget.setUrl(QUrl(url)) - + print("Create buffer: %s" % buffer_id) - + def resize_buffer(self, width, height): self.width = width self.height = height self.buffer_widget.resize(self.width, self.height) - + diff --git a/app/imageviewer/buffer.py b/app/imageviewer/buffer.py index dbef70c..b136a21 100644 --- a/app/imageviewer/buffer.py +++ b/app/imageviewer/buffer.py @@ -2,20 +2,20 @@ # -*- coding: utf-8 -*- # Copyright (C) 2018 Andy Stewart -# +# # Author: Andy Stewart # Maintainer: Andy Stewart -# +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . @@ -28,10 +28,10 @@ from buffer import Buffer class ImageViewerBuffer(Buffer): def __init__(self, buffer_id, url, width, height): Buffer.__init__(self, buffer_id, url, width, height, QColor(0, 0, 0, 255)) - + self.buffer_widget = ImageViewerWidget(url, QColor(0, 0, 0, 255)) self.buffer_widget.resize(self.width, self.height) - + def resize_buffer(self, width, height): self.width = width self.height = height @@ -40,16 +40,16 @@ class ImageViewerBuffer(Buffer): class ImageViewerWidget(QWidget): def __init__(self, image_path, background_color): QWidget.__init__(self) - + self.background_color = background_color self.load_image(image_path) - + def load_image(self, image_path): self.parent_dir = os.path.abspath(os.path.join(image_path, os.pardir)) self.image_name = os.path.basename(image_path) self.qimage = QPixmap(image_path) self.update() - + def load_next_image(self): files = [f for f in os.listdir(self.parent_dir) if os.path.isfile(os.path.join(self.parent_dir, f))] images = list(filter(lambda f: f.endswith(".jpg") or f.endswith(".png"), files)) @@ -59,9 +59,9 @@ class ImageViewerWidget(QWidget): image_index = 0 else: image_index += 1 - - self.load_image(os.path.join(self.parent_dir, images[image_index])) - + + self.load_image(os.path.join(self.parent_dir, images[image_index])) + def load_prev_image(self): files = [f for f in os.listdir(self.parent_dir) if os.path.isfile(os.path.join(self.parent_dir, f))] images = list(filter(lambda f: f.endswith(".jpg") or f.endswith(".png"), files)) @@ -71,15 +71,15 @@ class ImageViewerWidget(QWidget): image_index = len(images) - 1 else: image_index -= 1 - - self.load_image(os.path.join(self.parent_dir, images[image_index])) - + + self.load_image(os.path.join(self.parent_dir, images[image_index])) + def paintEvent(self, event): painter = QPainter(self) - + painter.setBrush(self.background_color) painter.drawRect(0, 0, self.rect().width(), self.rect().height()) - + width_scale = self.rect().width() * 1.0 / self.qimage.width() height_scale = self.rect().height() * 1.0 / self.qimage.height() image_scale = 1.0 @@ -87,31 +87,31 @@ class ImageViewerWidget(QWidget): image_scale = width_scale else: image_scale = height_scale - - render_width = image_scale * self.qimage.width() - render_height = image_scale * self.qimage.height() + + render_width = image_scale * self.qimage.width() + render_height = image_scale * self.qimage.height() render_x = (self.rect().width() - render_width) / 2 render_y = (self.rect().height() - render_height) / 2 painter.drawPixmap(QRect(render_x, render_y, render_width, render_height), self.qimage) - + painter.end() - + def keyPressEvent(self, event): if event.key() == Qt.Key_J: self.load_next_image() elif event.key() == Qt.Key_K: self.load_prev_image() - + if __name__ == "__main__": from PyQt5.QtWidgets import QApplication import sys import signal app = QApplication(sys.argv) - + test = ImageViewerWidget("/home/andy/rms/1.jpg", QColor(0, 0, 0, 255)) test.show() signal.signal(signal.SIGINT, signal.SIG_DFL) sys.exit(app.exec_()) - + diff --git a/app/videoplayer/buffer.py b/app/videoplayer/buffer.py index 92fc829..7aae06d 100644 --- a/app/videoplayer/buffer.py +++ b/app/videoplayer/buffer.py @@ -2,20 +2,20 @@ # -*- coding: utf-8 -*- # Copyright (C) 2018 Andy Stewart -# +# # Author: Andy Stewart # Maintainer: Andy Stewart -# +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . @@ -34,19 +34,19 @@ html_file = ''' margin: 0px; padding: 0px; } - + body { margin: 0px; padding: 0px; } - + video { width: 100%; height: 100%; margin: 0px; padding: 0px; } - + ::-webkit-scrollbar { display: none; } @@ -59,13 +59,13 @@ html_file = ''' class VideoPlayerBuffer(Buffer): def __init__(self, buffer_id, url, width, height): Buffer.__init__(self, buffer_id, url, width, height, QColor(255, 255, 255, 255)) - + self.buffer_widget = QWebView() self.buffer_widget.resize(self.width, self.height) self.buffer_widget.setHtml(html_file.replace("**********", url)) - + print("Create buffer: %s" % buffer_id) - + def resize_buffer(self, width, height): self.width = width self.height = height diff --git a/core/buffer.py b/core/buffer.py index 84573ae..b31d564 100644 --- a/core/buffer.py +++ b/core/buffer.py @@ -2,20 +2,20 @@ # -*- coding: utf-8 -*- # Copyright (C) 2018 Andy Stewart -# +# # Author: Andy Stewart # Maintainer: Andy Stewart -# +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . @@ -25,59 +25,59 @@ import functools import abc class postGui(QtCore.QObject): - - through_thread = QtCore.pyqtSignal(object, object) - + + through_thread = QtCore.pyqtSignal(object, object) + def __init__(self, inclass=True): super(postGui, self).__init__() self.through_thread.connect(self.on_signal_received) self.inclass = inclass - + def __call__(self, func): self._func = func - + @functools.wraps(func) def obj_call(*args, **kwargs): self.emit_signal(args, kwargs) return obj_call - + def emit_signal(self, args, kwargs): self.through_thread.emit(args, kwargs) - + def on_signal_received(self, args, kwargs): if self.inclass: obj, args = args[0], args[1:] self._func(obj, *args, **kwargs) - else: + else: self._func(*args, **kwargs) class Buffer(object): __metaclass__ = abc.ABCMeta - + def __init__(self, buffer_id, url, width, height, background_color): self.width = width self.height = height - + self.buffer_id = buffer_id self.url = url - + self.qimage = None self.buffer_widget = None self.background_color = background_color - + def resize_buffer(self, width, height): pass - + def handle_destroy(self): if self.buffer_widget != None: self.buffer_widget.destroy() - + if self.qimage != None: del self.qimage - + print("Destroy buffer: %s" % self.buffer_id) - - @postGui() + + @postGui() def update_content(self): if self.buffer_widget != None: qimage = QImage(self.width, self.height, QImage.Format_ARGB32) diff --git a/core/eaf.py b/core/eaf.py index e7c0802..2d5066c 100755 --- a/core/eaf.py +++ b/core/eaf.py @@ -2,20 +2,20 @@ # -*- coding: utf-8 -*- # Copyright (C) 2018 Andy Stewart -# +# # Author: Andy Stewart # Maintainer: Andy Stewart -# +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . @@ -33,7 +33,7 @@ import time import os,sys,inspect current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) parent_dir = os.path.dirname(current_dir) -sys.path.insert(0, parent_dir) +sys.path.insert(0, parent_dir) from app.browser.buffer import BrowserBuffer from app.imageviewer.buffer import ImageViewerBuffer from app.videoplayer.buffer import VideoPlayerBuffer @@ -44,18 +44,18 @@ EAF_OBJECT_NAME = "/com/lazycat/eaf" class EAF(dbus.service.Object): def __init__(self, args): global emacs_xid, emacs_width, emacs_height - + dbus.service.Object.__init__( self, dbus.service.BusName(EAF_DBUS_NAME, bus=dbus.SessionBus()), EAF_OBJECT_NAME) - + (emacs_xid, emacs_width, emacs_height) = (map(lambda x: int(x), args)) self.buffer_dict = {} self.view_dict = {} - + self.start_finish() - + @dbus.service.method(EAF_DBUS_NAME, in_signature="ss", out_signature="s") def new_buffer(self, buffer_id, url): global emacs_width, emacs_height @@ -68,7 +68,7 @@ class EAF(dbus.service.Object): else: return "Don't know how to open {0}".format(url) else: - from urllib.parse import urlparse + from urllib.parse import urlparse result = urlparse(url) if len(result.scheme) != 0: self.buffer_dict[buffer_id] = BrowserBuffer(buffer_id, result.geturl(), emacs_width, emacs_height) @@ -78,31 +78,31 @@ class EAF(dbus.service.Object): self.buffer_dict[buffer_id] = BrowserBuffer(buffer_id, result.geturl(), emacs_width, emacs_height) else: return "{0} is not valid url".format(url) - + return "" - + @dbus.service.method(EAF_DBUS_NAME, in_signature="s", out_signature="") def update_views(self, args): global emacs_xid - + view_infos = args.split(",") - + # Remove old key from view dict and destroy old view. for key in list(self.view_dict): if key not in view_infos: self.view_dict[key].handle_destroy() self.view_dict.pop(key, None) - + # Create new view and udpate in view dict. if view_infos != ['']: for view_info in view_infos: if view_info not in self.view_dict: view = View(emacs_xid, view_info) self.view_dict[view_info] = view - + view.trigger_mouse_event.connect(self.send_mouse_event_to_buffer) view.trigger_focus_event.connect(self.focus_emacs_buffer) - + @dbus.service.method(EAF_DBUS_NAME, in_signature="s", out_signature="") def kill_buffer(self, buffer_id): # Kill all view base on buffer_id. @@ -110,51 +110,51 @@ class EAF(dbus.service.Object): if buffer_id == self.view_dict[key].buffer_id: self.view_dict[key].handle_destroy() self.view_dict.pop(key, None) - + # Clean buffer from buffer dict. if buffer_id in self.buffer_dict: self.buffer_dict[buffer_id].handle_destroy() self.buffer_dict.pop(buffer_id, None) - - + + @dbus.service.method(EAF_DBUS_NAME, in_signature="s", out_signature="") def send_key(self, args): print("Send key: %s" % args) (buffer_id, event_string) = args.split(":") - + if buffer_id in self.buffer_dict: QApplication.sendEvent(self.buffer_dict[buffer_id].buffer_widget, fake_key_event(event_string)) - - @dbus.service.signal("com.lazycat.eaf") + + @dbus.service.signal("com.lazycat.eaf") def focus_emacs_buffer(self, message): print("************* %s" % message) - - @dbus.service.signal("com.lazycat.eaf") + + @dbus.service.signal("com.lazycat.eaf") def start_finish(self): pass - + def send_mouse_event_to_buffer(self, buffer_id, view_width, view_height, view_image_width, view_image_height, event): print("Send mouse: %s %s" % (buffer_id, event)) - + global emacs_xid - + if buffer_id in self.buffer_dict: if event.type() in [QEvent.MouseButtonPress, QEvent.MouseButtonRelease, QEvent.MouseMove, QEvent.MouseButtonDblClick]: # Get view render coordinate. view_render_x = (view_width - view_image_width) / 2 view_render_y = (view_height - view_image_height) / 2 - + # Just send event if response in view image area. if (event.x() >= view_render_x) and (event.x() <= view_render_x + view_image_width) and (event.y() >= view_render_y) and (event.y() <= view_render_y + view_image_height): view_sizes = list(map(lambda v: (v.width, v.height), self.view_dict.values())) - + buffer_width = emacs_width buffer_height = emacs_height - + if len(view_sizes) > 0: buffer_width, buffer_height = max(view_sizes, key=lambda size: size[0] * size[1]) - + width_scale = view_width * 1.0 / buffer_width height_scale = view_height * 1.0 / buffer_height image_scale = 1.0 @@ -166,42 +166,42 @@ class EAF(dbus.service.Object): new_event_x = (event.x() - view_render_x) / image_scale new_event_y = (event.y() - view_render_y) / image_scale new_event_pos = QPointF(new_event_x, new_event_y) - + new_event = QMouseEvent(event.type(), new_event_pos, event.button(), event.buttons(), event.modifiers()) - + QApplication.sendEvent(self.buffer_dict[buffer_id].buffer_widget, new_event) else: print("Do not send event, because event out of view's response area") else: QApplication.sendEvent(self.buffer_dict[buffer_id].buffer_widget, event) - + def update_buffers(self): global emacs_width, emacs_height - + while True: for buffer in list(self.buffer_dict.values()): # Get size list of buffer's views. view_sizes = list(map(lambda v: (v.width, v.height), self.view_dict.values())) - + # Init buffer size with emacs' size. buffer_width = emacs_width buffer_height = emacs_height - + # Update buffer size with max area view's size, # to make each view has the same rendering area after user do split operation in emacs. if len(view_sizes) > 0: buffer_width, buffer_height = max(view_sizes, key=lambda size: size[0] * size[1]) - + # Resize buffer. - buffer.resize_buffer(buffer_width, buffer_height) - + buffer.resize_buffer(buffer_width, buffer_height) + # Update buffer image. buffer.update_content() - + if buffer.qimage != None: # Render views. for view in list(self.view_dict.values()): @@ -220,29 +220,29 @@ class EAF(dbus.service.Object): # Update view. view.update() - + time.sleep(0.04) - + if __name__ == "__main__": import sys import signal - + DBusGMainLoop(set_as_default=True) # WARING: only use once in one process - + bus = dbus.SessionBus() if bus.request_name(EAF_DBUS_NAME) != dbus.bus.REQUEST_NAME_REPLY_PRIMARY_OWNER: print("EAF process has startup.") else: emacs_xid = 0 emacs_width = emacs_height = 0 - + app = QApplication(sys.argv) - + eaf = EAF(sys.argv[1:]) - + threading.Thread(target=eaf.update_buffers).start() - + print("EAF process start.") - + signal.signal(signal.SIGINT, signal.SIG_DFL) sys.exit(app.exec_()) diff --git a/core/fake_key_event.py b/core/fake_key_event.py index 5f6fe45..8bf05c4 100644 --- a/core/fake_key_event.py +++ b/core/fake_key_event.py @@ -82,5 +82,5 @@ def fake_key_event(event_string): text = event_string if event_string in qt_text_dict: text = qt_text_dict[event_string] - + return QKeyEvent(QEvent.KeyPress, qt_key_dict[event_string], Qt.NoModifier, text) diff --git a/core/view.py b/core/view.py index 794b884..329b498 100644 --- a/core/view.py +++ b/core/view.py @@ -2,20 +2,20 @@ # -*- coding: utf-8 -*- # Copyright (C) 2018 Andy Stewart -# +# # Author: Andy Stewart # Maintainer: Andy Stewart -# +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . @@ -26,20 +26,20 @@ from PyQt5.QtWidgets import QWidget from xutils import get_xlib_display class View(QWidget): - + trigger_mouse_event = QtCore.pyqtSignal(str, int, int, int, int, QEvent) trigger_focus_event = QtCore.pyqtSignal(str) - + def __init__(self, emacs_xid, view_info): super(View, self).__init__() - + self.emacs_xid = emacs_xid - + # Init widget attributes. self.setWindowFlags(Qt.FramelessWindowHint) self.setAttribute(Qt.WA_X11DoNotAcceptFocus, True) self.setContentsMargins(0, 0, 0, 0) - + # Init attributes. self.view_info = view_info (self.buffer_id, self.x, self.y, self.width, self.height) = view_info.split(":") @@ -47,65 +47,65 @@ class View(QWidget): self.y = int(self.y) self.width = int(self.width) self.height = int(self.height) - + self.qimage = None self.background_color = None - + # Show and resize. self.show() self.resize(self.width, self.height) - + self.installEventFilter(self) - + print("Create view: %s" % self.view_info) - + def eventFilter(self, obj, event): if event.type() in [QEvent.MouseButtonPress, QEvent.MouseButtonRelease, QEvent.MouseMove, QEvent.MouseButtonDblClick, QEvent.Wheel]: self.trigger_mouse_event.emit(self.buffer_id, self.width, self.height, self.qimage.width(), self.qimage.height(), event) self.trigger_focus_event.emit("{0},{1}".format(event.globalX(), event.globalY())) - + return False - + def paintEvent(self, event): # Init painter. painter = QPainter(self) - + # Render background. if self.background_color != None: painter.setBrush(self.background_color) painter.drawRect(0, 0, self.width, self.height) - + # Render buffer image in center of view. if self.qimage != None: render_x = (self.width - self.qimage.width()) / 2 render_y = (self.height - self.qimage.height()) / 2 painter.drawImage(QtCore.QRect(render_x, render_y, self.qimage.width(), self.qimage.height()), self.qimage) - + # End painter. painter.end() - + def showEvent(self, event): # NOTE: we must reparent after widget show, otherwise reparent operation maybe failed. self.reparent() - + def reparent(self): xlib_display = get_xlib_display() - + view_xid = self.winId().__int__() view_xwindow = xlib_display.create_resource_object("window", view_xid) emacs_xwindow = xlib_display.create_resource_object("window", self.emacs_xid) - + view_xwindow.reparent(emacs_xwindow, self.x, self.y) - + xlib_display.sync() - + def handle_destroy(self): if self.qimage != None: del self.qimage - + self.destroy() - + print("Destroy view: %s" % self.view_info) - + diff --git a/core/xutils.py b/core/xutils.py index d634feb..3719440 100644 --- a/core/xutils.py +++ b/core/xutils.py @@ -2,20 +2,20 @@ # -*- coding: utf-8 -*- # Copyright (C) 2011 ~ 2014 Andy Stewart -# +# # Author: Andy Stewart # Maintainer: Andy Stewart -# +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with this program. If not, see . @@ -23,12 +23,12 @@ xlib_display = None def get_xlib_display(): global xlib_display - + if xlib_display == None: from Xlib import display xlib_display = display.Display() - - return xlib_display + + return xlib_display def grab_focus(window_id): global xlib_display @@ -38,7 +38,7 @@ def grab_focus(window_id): xwindow.set_input_focus(X.RevertToNone, X.CurrentTime) xwindow.configure(stack_mode=X.Above) - + xlib_display.sync() def get_parent_window_id(window_id):