Advertisement
ReverseFlux

webkit

Feb 3rd, 2017
184
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 36.68 KB | None | 0 0
  1. #browser.py
  2. #! /usr/bin/env python
  3. # -*- coding: utf-8 -*-
  4.  
  5. # Copyright (C) 2011 ~ 2014 Andy Stewart
  6. #
  7. # Author: Andy Stewart <lazycat.manatee@gmail.com>
  8. # Maintainer: Andy Stewart <lazycat.manatee@gmail.com>
  9. #
  10. # This program is free software: you can redistribute it and/or modify
  11. # it under the terms of the GNU General Public License as published by
  12. # the Free Software Foundation, either version 3 of the License, or
  13. # any later version.
  14. #
  15. # This program is distributed in the hope that it will be useful,
  16. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. # GNU General Public License for more details.
  19. #
  20. # You should have received a copy of the GNU General Public License
  21. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  22.  
  23. import os
  24. from PyQt5 import QtCore
  25. from PyQt5.QtNetwork import QNetworkCookieJar, QNetworkCookie
  26. from PyQt5.QtCore import QCoreApplication, QEvent
  27. from PyQt5.QtNetwork import QNetworkProxy
  28. if os.name == 'posix':
  29. QCoreApplication.setAttribute(QtCore.Qt.AA_X11InitThreads, True)
  30.  
  31. from PyQt5.QtWebKitWidgets import QWebView, QWebPage
  32. from PyQt5.QtWebKit import QWebSettings
  33. from PyQt5.QtWidgets import QApplication
  34. from PyQt5.QtCore import QUrl, Qt
  35. from PyQt5 import QtGui
  36. import time
  37. import os
  38. from epc.server import ThreadingEPCServer
  39. import threading
  40. from PyQt5.QtWidgets import QWidget
  41. from PyQt5.QtGui import QPainter, QImage
  42. import functools
  43. from utils import get_parent_dir
  44. from xutils import get_xlib_display, grab_focus, ActiveWindowWatcher, get_parent_window_id
  45. from send_key import send_string
  46.  
  47. class postGui(QtCore.QObject):
  48.  
  49. throughThread = QtCore.pyqtSignal(object, object)
  50.  
  51. def __init__(self, inclass=True):
  52. super(postGui, self).__init__()
  53. self.throughThread.connect(self.onSignalReceived)
  54. self.inclass = inclass
  55.  
  56. def __call__(self, func):
  57. self._func = func
  58.  
  59. @functools.wraps(func)
  60. def objCall(*args, **kwargs):
  61. self.emitSignal(args, kwargs)
  62. return objCall
  63.  
  64. def emitSignal(self, args, kwargs):
  65. self.throughThread.emit(args, kwargs)
  66.  
  67. def onSignalReceived(self, args, kwargs):
  68. if self.inclass:
  69. obj, args = args[0], args[1:]
  70. self._func(obj, *args, **kwargs)
  71. else:
  72. self._func(*args, **kwargs)
  73.  
  74. class WebPage(QWebPage):
  75. def __init__(self):
  76. super(WebPage, self).__init__()
  77.  
  78. def acceptNavigationRequest(self, frame, request, type):
  79. # Handle myself if got user event.
  80. if type == QWebPage.NavigationTypeLinkClicked:
  81. if self.view().press_ctrl_flag:
  82. call_method("open-url", [request.url().toString()])
  83. else:
  84. self.view().load(request.url())
  85.  
  86. # Return False to stop default behavior.
  87. return False
  88.  
  89. # Otherwise, use default behavior.
  90. return QWebPage.acceptNavigationRequest(self, frame, request, type)
  91.  
  92. def javaScriptConsoleMessage(self, msg, lineNumber, sourceID):
  93. global print_console_info
  94. if print_console_info:
  95. call_message("JsConsole(%s:%d): %s" % (sourceID, lineNumber, msg))
  96.  
  97. class BrowserBuffer(QWebView):
  98.  
  99. redrawScreenshot = QtCore.pyqtSignal(object)
  100. updateProgress = QtCore.pyqtSignal()
  101.  
  102. def __init__(self, buffer_id, buffer_width, buffer_height):
  103. super(BrowserBuffer, self).__init__()
  104.  
  105. self.buffer_id = buffer_id
  106. self.buffer_width = buffer_width
  107. self.buffer_height = buffer_height
  108.  
  109. self.setPage(WebPage())
  110. self.page().mainFrame().setScrollBarPolicy(Qt.Horizontal, Qt.ScrollBarAlwaysOff)
  111. cookie_jar.restore_cookies()
  112. self.page().networkAccessManager().setCookieJar(cookie_jar)
  113. self.page().userAgentForUrl = self.customize_user_agent
  114. self.settings().setUserStyleSheetUrl(QUrl.fromLocalFile(os.path.join(get_parent_dir(__file__), "theme.css")))
  115. self.settings().setAttribute(QWebSettings.PluginsEnabled, True)
  116. self.settings().setAttribute(QWebSettings.JavascriptEnabled, True)
  117. self.settings().setAttribute(QWebSettings.JavascriptCanOpenWindows, True)
  118. self.settings().setFontFamily(QWebSettings.StandardFont, "Sans")
  119.  
  120. self.adjust_size(self.buffer_width, self.buffer_height)
  121.  
  122. self.view_dict = {}
  123.  
  124. self.titleChanged.connect(self.change_title)
  125.  
  126. self.press_ctrl_flag = False
  127.  
  128. self.loading_flag = False
  129. self.loading_percent = 0
  130.  
  131. self.loadFinished.connect(self.handle_load_finished)
  132. self.loadStarted.connect(self.handle_load_started)
  133. self.loadProgress.connect(self.handle_load_progress)
  134.  
  135. def handle_load_started(self, *args):
  136. self.loading_flag = True
  137. self.loading_percent = 0
  138. self.updateProgress.emit()
  139.  
  140. def handle_load_finished(self, *args):
  141. self.loading_flag = False
  142. self.loading_percent = 100
  143. self.updateProgress.emit()
  144.  
  145. def handle_load_progress(self, percent):
  146. self.loading_flag = True
  147. self.loading_percent = percent
  148. self.updateProgress.emit()
  149.  
  150. def customize_user_agent(self, url):
  151. return "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36"
  152.  
  153. def change_title(self, title):
  154. call_method("change-buffer-title", [self.buffer_id, title])
  155.  
  156. def eventFilter(self, obj, event):
  157. if event.type() in [QEvent.KeyPress, QEvent.KeyRelease,
  158. QEvent.MouseButtonPress, QEvent.MouseButtonRelease,
  159. QEvent.MouseMove, QEvent.MouseButtonDblClick, QEvent.Wheel,
  160. QEvent.InputMethod, QEvent.InputMethodQuery, QEvent.ShortcutOverride,
  161. QEvent.ActivationChange, QEvent.Enter, QEvent.WindowActivate,
  162. ]:
  163. QApplication.sendEvent(self, event)
  164.  
  165. if event.type() == QEvent.KeyPress and event.key() == QtCore.Qt.Key_Control:
  166. self.press_ctrl_flag = True
  167. elif event.type() == QEvent.KeyRelease and event.key() == QtCore.Qt.Key_Control:
  168. self.press_ctrl_flag = False
  169.  
  170. global emacs_xwindow_id
  171.  
  172. xlib_display = get_xlib_display()
  173. xwindow = xlib_display.create_resource_object("window", emacs_xwindow_id)
  174.  
  175. mask = []
  176. event_key = event.text()
  177. if event.modifiers() & QtCore.Qt.AltModifier == QtCore.Qt.AltModifier:
  178. mask.append("Alt")
  179. elif event.modifiers() & QtCore.Qt.ControlModifier == QtCore.Qt.ControlModifier:
  180. mask.append("Ctrl")
  181. elif event.modifiers() & QtCore.Qt.ShiftModifier == QtCore.Qt.ShiftModifier:
  182. mask.append("Shift")
  183. elif event.modifiers() & QtCore.Qt.MetaModifier == QtCore.Qt.MetaModifier:
  184. mask.append("Super")
  185.  
  186. send_string(xwindow, event_key, mask, event.type() == QEvent.KeyPress)
  187.  
  188. xlib_display.sync()
  189. else:
  190. if event.type() not in [12, 77]:
  191. call_method("%s %s" % (event.type(), event))
  192.  
  193. return False
  194.  
  195. def add_view(self, view_id, x, y, w, h):
  196. view = BrowserView(self, view_id)
  197. self.view_dict[view_id] = view
  198. self.update_view(view_id, x, y, w, h)
  199.  
  200. view.show()
  201.  
  202. def remove_view(self, view_id):
  203. if view_id in self.view_dict:
  204. self.view_dict[view_id].remove()
  205. self.view_dict.pop(view_id)
  206.  
  207. def update_view(self, view_id, x, y, w, h):
  208. self.view_dict[view_id].moveresize(x, y, w, h)
  209.  
  210. def remove_all_views(self):
  211. for view_id in self.view_dict.keys():
  212. self.remove_view(view_id)
  213.  
  214. def adjust_size(self, width, height):
  215. self.buffer_width = width
  216. self.buffer_height = height
  217. self.resize(self.buffer_width, self.buffer_height)
  218.  
  219. @postGui()
  220. def redraw(self):
  221. if len(self.view_dict) > 0:
  222. qimage = QImage(self.buffer_width, self.buffer_height, QImage.Format_ARGB32)
  223. self.render(qimage)
  224.  
  225. self.redrawScreenshot.emit(qimage)
  226.  
  227. @postGui()
  228. def open_url(self, url):
  229. self.load(QUrl(url))
  230.  
  231. class BrowserView(QWidget):
  232. def __init__(self, browser_buffer, view_id):
  233. super(BrowserView, self).__init__()
  234.  
  235. self.browser_buffer = browser_buffer
  236. self.view_id = view_id
  237.  
  238. self.setWindowFlags(Qt.FramelessWindowHint)
  239. self.setAttribute(Qt.WA_TranslucentBackground, True)
  240.  
  241. self.setContentsMargins(0, 0, 0, 0)
  242.  
  243. self.browser_buffer.redrawScreenshot.connect(self.updateView)
  244. self.browser_buffer.updateProgress.connect(self.updateProgress)
  245.  
  246. self.qimage = None
  247.  
  248. self.installEventFilter(browser_buffer)
  249.  
  250. def remove(self):
  251. self.browser_buffer.redrawScreenshot.disconnect(self.updateView)
  252. self.destroy()
  253.  
  254. def paintEvent(self, event):
  255. painter = QPainter(self)
  256.  
  257. if self.qimage:
  258. painter.drawImage(QtCore.QRect(0, 0, self.width(), self.height()), self.qimage)
  259. else:
  260. painter.setBrush(QtGui.QColor(255, 255, 255, 255))
  261. painter.drawRect(0, 0, self.browser_buffer.buffer_width, self.browser_buffer.buffer_height)
  262.  
  263. if self.browser_buffer.loading_flag:
  264. painter.setPen(QtGui.QColor(10, 138, 255, 255))
  265. painter.setBrush(QtGui.QColor(10, 138, 255, 255))
  266. painter.drawRect(0, 0, self.browser_buffer.buffer_width * self.browser_buffer.loading_percent / 100, 1)
  267.  
  268. painter.end()
  269.  
  270. @postGui()
  271. def updateView(self, qimage):
  272. self.qimage = qimage
  273. self.update()
  274.  
  275. @postGui()
  276. def updateProgress(self):
  277. self.update()
  278.  
  279. def moveresize(self, x, y, w, h):
  280. self.resize(w, h)
  281. self.reparent(x, y)
  282.  
  283. def adjust_size(self, x, y, w, h):
  284. self.moveresize(x, y, w, h)
  285. self.browser_buffer.adjust_size(w, h)
  286.  
  287. def reparent(self, x, y):
  288. xlib_display = get_xlib_display()
  289.  
  290. browser_xwindow_id = self.winId().__int__()
  291. browser_xwindow = xlib_display.create_resource_object("window", browser_xwindow_id)
  292. emacs_xwindow = xlib_display.create_resource_object("window", emacs_xwindow_id)
  293.  
  294. browser_xwindow.reparent(emacs_xwindow, x, y)
  295.  
  296. xlib_display.sync()
  297.  
  298. class CookieJar(QNetworkCookieJar):
  299.  
  300. def __init__(self, parent = None):
  301. QNetworkCookieJar.__init__(self, parent)
  302. self.cookie_path = os.path.expanduser("~/.emacs.d/deepin-emacs/webkit-cookies")
  303.  
  304. def save_cookies(self):
  305. allCookies = QNetworkCookieJar.allCookies(self)
  306.  
  307. cookie_dir = os.path.dirname(self.cookie_path)
  308. if not os.path.exists(cookie_dir):
  309. os.makedirs(cookie_dir)
  310.  
  311. with open(self.cookie_path, 'w') as f:
  312. lines = ''
  313. for cookie in allCookies:
  314. lines = lines + cookie.toRawForm() + '\r\n'
  315. f.writelines(lines)
  316.  
  317. def restore_cookies(self):
  318. if os.path.exists(self.cookie_path):
  319. with open(self.cookie_path, 'r') as f:
  320. lines = ''
  321. for line in f:
  322. lines = lines + line
  323. allCookies = QNetworkCookie.parseCookies(lines)
  324. QNetworkCookieJar.setAllCookies(self, allCookies)
  325.  
  326. if __name__ == '__main__':
  327. import sys
  328. import signal
  329.  
  330. app = QApplication(sys.argv)
  331.  
  332. server = ThreadingEPCServer(('localhost', 0), log_traceback=True)
  333.  
  334. server_thread = threading.Thread(target=server.serve_forever)
  335. server_thread.allow_reuse_address = True
  336.  
  337. emacs_xwindow_id = 0
  338.  
  339. buffer_dict = {}
  340.  
  341. cookie_jar = CookieJar()
  342.  
  343. print_console_info = False
  344.  
  345. if len(sys.argv) >= 2 and sys.argv[1] == "--enable-proxy":
  346. QNetworkProxy.setApplicationProxy(QNetworkProxy(QNetworkProxy.Socks5Proxy, "127.0.0.1", 7070))
  347.  
  348. def call_message(message):
  349. call_method("message", [message])
  350.  
  351. def call_method(method_name, args):
  352. handler = server.clients[0]
  353. handler.call(method_name, args)
  354.  
  355. def handle_active_window(active_window_id):
  356. global emacs_xwindow_id
  357.  
  358. emacs_real_id = get_parent_window_id(emacs_xwindow_id)
  359.  
  360. call_method("message", ["handle_active_window: %s %s %s" % (active_window_id, emacs_xwindow_id, emacs_real_id)])
  361.  
  362. if active_window_id == emacs_real_id:
  363. call_method("focus-browser-view", [])
  364.  
  365. @postGui(False)
  366. def init(emacs_xid):
  367. global emacs_xwindow_id
  368.  
  369. emacs_xwindow_id = int(emacs_xid)
  370.  
  371. # NOTE: every epc method must should wrap with postGui.
  372. # Because epc server is running in sub-thread.
  373. @postGui(False)
  374. def create_buffer(buffer_id, buffer_url, buffer_width, buffer_height):
  375. if not buffer_id in buffer_dict:
  376. buffer = BrowserBuffer(buffer_id, buffer_width, buffer_height)
  377. buffer.open_url(buffer_url)
  378. buffer_dict[buffer_id] = buffer
  379.  
  380. @postGui(False)
  381. def remove_buffer(buffer_id):
  382. if buffer_id in buffer_dict:
  383. cookie_jar.save_cookies()
  384.  
  385. buffer = buffer_dict[buffer_id]
  386. buffer.remove_all_views()
  387. buffer_dict.pop(buffer_id)
  388. buffer.destroy()
  389.  
  390. call_message("Remove buffer %s" % buffer_id)
  391.  
  392. @postGui(False)
  393. def adjust_size(buffer_id, w, h):
  394. if buffer_id in buffer_dict:
  395. buffer_dict[buffer_id].adjust_size(w, h)
  396.  
  397. @postGui(False)
  398. def update_views(view_infos):
  399. buffer_view_dict = {}
  400.  
  401. for view_info in view_infos:
  402. [buffer_id, x, y, w, h] = view_info
  403. view_id = "%s_%s" % (x, y)
  404. if buffer_id in buffer_dict:
  405. if not buffer_id in buffer_view_dict:
  406. buffer_view_dict[buffer_id] = {}
  407.  
  408. buffer_view_dict[buffer_id][view_id] = (x, y, w, h)
  409. else:
  410. call_message("Buffer id %s is not exist!" % buffer_id)
  411.  
  412. for buffer in buffer_dict.values():
  413. if buffer.buffer_id in buffer_view_dict:
  414. emacs_view_ids = buffer_view_dict[buffer.buffer_id].keys()
  415. buffer_view_ids = buffer.view_dict.keys()
  416.  
  417. for emacs_view_id in emacs_view_ids:
  418. (x, y, w, h) = buffer_view_dict[buffer.buffer_id][emacs_view_id]
  419. # Update view.
  420. if emacs_view_id in buffer_view_ids:
  421. buffer.update_view(emacs_view_id, x, y, w, h)
  422.  
  423. # Create view.
  424. else:
  425. buffer.add_view(emacs_view_id, x, y, w, h)
  426. for buffer_view_id in buffer_view_ids:
  427. # Remove view.
  428. if buffer_view_id not in emacs_view_ids:
  429. buffer.remove_view(buffer_view_id)
  430. else:
  431. buffer.remove_all_views()
  432.  
  433. @postGui(False)
  434. def focus_view(buffer_id, x, y, w, h):
  435. if buffer_id in buffer_dict:
  436. buffer = buffer_dict[buffer_id]
  437. view_id = "%s_%s" % (x, y)
  438.  
  439. if view_id in buffer.view_dict:
  440. view = buffer.view_dict[view_id]
  441. view_xwindow_id = view.winId().__int__()
  442. grab_focus(view_xwindow_id)
  443.  
  444. def toggle_console_info():
  445. global print_console_info
  446.  
  447. print_console_info = not print_console_info
  448.  
  449. def update_buffer():
  450. while True:
  451. for buffer in buffer_dict.values():
  452. buffer.redraw()
  453.  
  454. time.sleep(0.05)
  455.  
  456. server_thread.start()
  457. server.print_port()
  458.  
  459. server.register_function(init)
  460. server.register_function(create_buffer)
  461. server.register_function(remove_buffer)
  462. server.register_function(adjust_size)
  463. server.register_function(update_views)
  464. server.register_function(focus_view)
  465. server.register_function(toggle_console_info)
  466.  
  467. threading.Thread(target=update_buffer).start()
  468.  
  469. active_window_watcher = ActiveWindowWatcher()
  470. active_window_watcher.activeWindowChanged.connect(handle_active_window)
  471. active_window_watcher.start()
  472.  
  473. signal.signal(signal.SIGINT, signal.SIG_DFL)
  474. sys.exit(app.exec_())
  475.  
  476. ::-webkit-scrollbar {
  477. width: 12px;
  478. }
  479.  
  480. ::-webkit-scrollbar-track {
  481. -webkit-box-shadow: inset 0 0 3px rgba(0,0,0,0.3);
  482. border-radius: 4px;
  483. }
  484.  
  485. ::-webkit-scrollbar-thumb {
  486. border-radius: 4px;
  487. -webkit-box-shadow: inset 0 0 3px rgba(0,0,0,0.5);
  488. }
  489.  
  490. #send_key.py
  491. #! /usr/bin/env python
  492. # -*- coding: utf-8 -*-
  493.  
  494. # Copyright (C) 2011 ~ 2014 Andy Stewart
  495. #
  496. # Author: Andy Stewart <lazycat.manatee@gmail.com>
  497. # Maintainer: Andy Stewart <lazycat.manatee@gmail.com>
  498. #
  499. # This program is free software: you can redistribute it and/or modify
  500. # it under the terms of the GNU General Public License as published by
  501. # the Free Software Foundation, either version 3 of the License, or
  502. # any later version.
  503. #
  504. # This program is distributed in the hope that it will be useful,
  505. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  506. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  507. # GNU General Public License for more details.
  508. #
  509. # You should have received a copy of the GNU General Public License
  510. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  511.  
  512. import Xlib.display
  513. import Xlib.X
  514. import Xlib.XK
  515. import Xlib.protocol.event
  516. from xutils import get_xlib_display
  517.  
  518. special_X_keysyms = {
  519. ' ' : "space",
  520. '\t' : "Tab",
  521. '\n' : "Return", # for some reason this needs to be cr, not lf
  522. '\r' : "Return",
  523. '\e' : "Escape",
  524. '!' : "exclam",
  525. '#' : "numbersign",
  526. '%' : "percent",
  527. '$' : "dollar",
  528. '&' : "ampersand",
  529. '"' : "quotedbl",
  530. '\'' : "apostrophe",
  531. '(' : "parenleft",
  532. ')' : "parenright",
  533. '*' : "asterisk",
  534. '=' : "equal",
  535. '+' : "plus",
  536. ',' : "comma",
  537. '-' : "minus",
  538. '.' : "period",
  539. '/' : "slash",
  540. ':' : "colon",
  541. ';' : "semicolon",
  542. '<' : "less",
  543. '>' : "greater",
  544. '?' : "question",
  545. '@' : "at",
  546. '[' : "bracketleft",
  547. ']' : "bracketright",
  548. '\\' : "backslash",
  549. '^' : "asciicircum",
  550. '_' : "underscore",
  551. '`' : "grave",
  552. '{' : "braceleft",
  553. '|' : "bar",
  554. '}' : "braceright",
  555. '~' : "asciitilde"
  556. }
  557.  
  558. def get_keysym(ch):
  559. keysym = Xlib.XK.string_to_keysym(ch)
  560. if keysym == 0:
  561. # Unfortunately, although this works to get the correct keysym
  562. # i.e. keysym for '#' is returned as "numbersign"
  563. # the subsequent display.keysym_to_keycode("numbersign") is 0.
  564. if ch in special_X_keysyms:
  565. special = special_X_keysyms[ch]
  566. keysym = Xlib.XK.string_to_keysym(special)
  567. return keysym
  568.  
  569. def send_string(window, str, modifiers, press=True):
  570. xlib_display = get_xlib_display()
  571.  
  572. mask = 0
  573. for modifier in modifiers:
  574. if modifier == "Ctrl":
  575. mask |= Xlib.X.ControlMask
  576. elif modifier == "Alt":
  577. mask |= Xlib.X.Mod1Mask
  578. elif modifier == "Shift":
  579. mask |= Xlib.X.ShiftMask
  580. elif modifier == "Super":
  581. mask |= Xlib.X.Mod4Mask
  582.  
  583. keycode = xlib_display.keysym_to_keycode(get_keysym(str))
  584.  
  585. if press:
  586. event_type = Xlib.protocol.event.KeyPress
  587. else:
  588. event_type = Xlib.protocol.event.KeyRelease
  589.  
  590. event = event_type(
  591. root=xlib_display.screen().root,
  592. window=window,
  593. child=Xlib.X.NONE,
  594. same_screen=1,
  595. root_x=0,
  596. root_y=0,
  597. event_x=0,
  598. event_y=0,
  599. state=mask,
  600. detail=keycode,
  601. time=Xlib.X.CurrentTime,
  602. )
  603. window.send_event(event, propagate=True)
  604.  
  605. if __name__ == "__main__":
  606. xlib_display = get_xlib_display()
  607. xwindow = xlib_display.create_resource_object("window", 73400407)
  608.  
  609. # send_string(xwindow, "x", ["Ctrl"], False)
  610. # send_string(xwindow, "x", ["Ctrl"], True)
  611.  
  612. # send_string(xwindow, "h", [], False)
  613. # send_string(xwindow, "h", [], True)
  614.  
  615. send_string(xwindow, "y", ["Super"], False)
  616. send_string(xwindow, "y", ["Super"], True)
  617.  
  618. xlib_display.sync()
  619. #utils.py
  620. #! /usr/bin/env python
  621. # -*- coding: utf-8 -*-
  622.  
  623. # Copyright (C) 2011 ~ 2014 Andy Stewart
  624. #
  625. # Author: Andy Stewart <lazycat.manatee@gmail.com>
  626. # Maintainer: Andy Stewart <lazycat.manatee@gmail.com>
  627. #
  628. # This program is free software: you can redistribute it and/or modify
  629. # it under the terms of the GNU General Public License as published by
  630. # the Free Software Foundation, either version 3 of the License, or
  631. # any later version.
  632. #
  633. # This program is distributed in the hope that it will be useful,
  634. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  635. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  636. # GNU General Public License for more details.
  637. #
  638. # You should have received a copy of the GNU General Public License
  639. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  640.  
  641. import os
  642.  
  643. def get_parent_dir(filepath, level=1):
  644. '''
  645. Get parent directory with given return level.
  646. @param filepath: Filepath.
  647. @param level: Return level, default is 1
  648. @return: Return parent directory with given return level.
  649. '''
  650. parent_dir = os.path.realpath(filepath)
  651.  
  652. while(level > 0):
  653. parent_dir = os.path.dirname(parent_dir)
  654. level -= 1
  655.  
  656. return parent_dir
  657.  
  658.  
  659. ;;; webkit.el --- Running WebKit browser in Emacs
  660.  
  661. ;; Filename: webkit.el
  662. ;; Description: Running WebKit browser in Emacs
  663. ;; Author: Andy Stewart <lazycat.manatee@gmail.com>
  664. ;; Maintainer: Andy Stewart <lazycat.manatee@gmail.com>
  665. ;; Copyright (C) 2014, Andy Stewart, all rights reserved.
  666. ;; Created: 2014-01-02 21:51:17
  667. ;; Version: 0.1
  668. ;; Last-Updated: 2014-01-02 21:51:17
  669. ;; By: Andy Stewart
  670. ;; URL: http://www.emacswiki.org/emacs/download/webkit.el
  671. ;; Keywords:
  672. ;; Compatibility: GNU Emacs 24.3.50.1
  673. ;;
  674. ;; Features that might be required by this library:
  675. ;;
  676. ;;
  677. ;;
  678.  
  679. ;;; This file is NOT part of GNU Emacs
  680.  
  681. ;;; License
  682. ;;
  683. ;; This program is free software; you can redistribute it and/or modify
  684. ;; it under the terms of the GNU General Public License as published by
  685. ;; the Free Software Foundation; either version 3, or (at your option)
  686. ;; any later version.
  687.  
  688. ;; This program is distributed in the hope that it will be useful,
  689. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  690. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  691. ;; GNU General Public License for more details.
  692.  
  693. ;; You should have received a copy of the GNU General Public License
  694. ;; along with this program; see the file COPYING. If not, write to
  695. ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
  696. ;; Floor, Boston, MA 02110-1301, USA.
  697.  
  698. ;;; Commentary:
  699. ;;
  700. ;; Running WebKit browser in Emacs
  701. ;;
  702.  
  703. ;;; Installation:
  704. ;;
  705. ;; NOTE: just webkit.el can't work, you need install below depends first:
  706. ;; * PyQt5: http://www.riverbankcomputing.co.uk/software/pyqt/intro
  707. ;; * Python-Xlib: https://pypi.python.org/pypi/python-xlib
  708. ;; * Python-EPC: https://github.com/tkf/python-epc
  709. ;;
  710. ;; Detail description please look: http://www.emacswiki.org/emacs/WebKit
  711. ;;
  712. ;; Then put webkit.el to your load-path.
  713. ;; The load-path is usually ~/elisp/.
  714. ;; It's set in your ~/.emacs like this:
  715. ;; (add-to-list 'load-path (expand-file-name "~/elisp"))
  716. ;;
  717. ;; And the following to your ~/.emacs startup file.
  718. ;;
  719. ;; (require 'webkit)
  720. ;;
  721. ;; Quick start:
  722. ;;
  723. ;; M-x webkit-open-url
  724. ;;
  725.  
  726. ;;; Customize:
  727. ;;
  728. ;;
  729. ;;
  730. ;; All of the above can customize by:
  731. ;; M-x customize-group RET webkit RET
  732. ;;
  733.  
  734. ;;; Change log:
  735. ;;
  736. ;; 2014/01/02
  737. ;; * First released.
  738. ;;
  739.  
  740. ;;; Acknowledgements:
  741. ;;
  742. ;;
  743. ;;
  744.  
  745. ;;; TODO
  746. ;;
  747. ;;
  748. ;;
  749.  
  750. ;;; Require
  751.  
  752. (require 'epc)
  753. (when noninteractive
  754. (load "subr")
  755. (load "byte-run"))
  756. (eval-when-compile (require 'cl))
  757.  
  758. ;;; Code:
  759.  
  760. (defcustom webkit-mode-hook '()
  761. "WebKit mode hook."
  762. :type 'hook
  763. :group 'webkit-mode)
  764.  
  765. (defvar webkit-mode-map
  766. (let ((map (make-sparse-keymap)))
  767. map)
  768. "Keymap used by `webkit-mode'.")
  769.  
  770. (define-derived-mode webkit-mode text-mode "WebKit"
  771. (interactive)
  772. (kill-all-local-variables)
  773. (setq major-mode 'webkit-mode)
  774. (setq mode-name "WebKit")
  775. (set (make-local-variable 'buffer-id) (webkit-generate-id))
  776. (use-local-map webkit-mode-map)
  777. (run-hooks 'webkit-mode-hook))
  778.  
  779. (defun webkit-get-window-allocation (&optional window)
  780. (let* ((window-edges (window-inside-pixel-edges window))
  781. (x (nth 0 window-edges))
  782. (y (nth 1 window-edges))
  783. (w (- (nth 2 window-edges) x))
  784. (h (- (nth 3 window-edges) y))
  785. )
  786. (list x y w h)))
  787.  
  788. (defun webkit-get-emacs-xid ()
  789. (frame-parameter nil 'window-id))
  790.  
  791. (random t)
  792.  
  793. (defun webkit-generate-id ()
  794. (format "%04x%04x-%04x-%04x-%04x-%06x%06x"
  795. (random (expt 16 4))
  796. (random (expt 16 4))
  797. (random (expt 16 4))
  798. (random (expt 16 4))
  799. (random (expt 16 4))
  800. (random (expt 16 6))
  801. (random (expt 16 6)) ))
  802.  
  803. (defvar webkit-enable-proxy-p nil)
  804. (defvar webkit-proxy-config-path "~/.emacs.d/deepin-emacs/webkit-proxy")
  805. (defun webkit-save-proxy-config ()
  806. (with-current-buffer (find-file-noselect webkit-proxy-config-path)
  807. (erase-buffer)
  808. (insert (prin1-to-string webkit-enable-proxy-p))
  809. (let ((delete-old-versions nil))
  810. (save-buffer 0))))
  811.  
  812. (defun webkit-restore-proxy-config ()
  813. (if (file-exists-p webkit-proxy-config-path)
  814. (setq webkit-enable-proxy-p
  815. (read
  816. (with-temp-buffer
  817. (insert-file-contents webkit-proxy-config-path)
  818. (buffer-string))))))
  819.  
  820. (webkit-restore-proxy-config)
  821.  
  822. (defvar pyepc-file (expand-file-name "browser.py" (file-name-directory load-file-name)))
  823.  
  824. (defvar pyepc-browser
  825. (let* ((browser
  826. (epc:start-epc (or (getenv "PYTHON") "python")
  827. (list pyepc-file
  828. (if webkit-enable-proxy-p "--enable-proxy" "--disable-proxy")))))
  829. (epc:call-deferred browser 'init (list (webkit-get-emacs-xid)))
  830. browser))
  831.  
  832. (defvar webkit-buffer-dict (make-hash-table :test 'equal))
  833.  
  834. (defvar webkit-history-urls-path "~/.emacs.d/deepin-emacs/webkit-history")
  835. (defvar webkit-history-urls (make-hash-table :test 'equal))
  836.  
  837. (defvar webkit-title-length 30)
  838.  
  839. (defvar webkit-tab-index 0)
  840.  
  841. (defun webkit-create-buffer (url)
  842. (setq webkit-tab-index (+ webkit-tab-index 1))
  843. (let ((webkit-buffer (generate-new-buffer (concat (truncate-string-to-width url webkit-title-length)))))
  844. (with-current-buffer webkit-buffer
  845. (webkit-mode)
  846. (set (make-local-variable 'buffer-url) url)
  847. (puthash buffer-id webkit-buffer webkit-buffer-dict)
  848. )
  849. webkit-buffer))
  850.  
  851. (defun webkit-get-url-name (url)
  852. (car (last (split-string url "://")))
  853. )
  854.  
  855. (defun webkit-get-url-history (url-name)
  856. (if webkit-history-urls
  857. (gethash url-name webkit-history-urls)
  858. nil)
  859. )
  860.  
  861. (defun webkit-change-buffer-title (id title)
  862. (let* ((buffer (gethash id webkit-buffer-dict)))
  863. (with-current-buffer buffer
  864. ;; Rename buffer title.
  865. (rename-buffer (truncate-string-to-width title webkit-title-length))
  866.  
  867. ;; Record url title in history.
  868. (let* ((url-name (webkit-get-url-name buffer-url))
  869. (url-history (webkit-get-url-history url-name)))
  870. (if url-history
  871. (let ((url-number (car url-history))
  872. (url-title (cdr url-history)))
  873. (puthash url-name (list url-number title) webkit-history-urls)
  874. (webkit-save-history-urls))))
  875. )
  876. )
  877. )
  878.  
  879. (defun webkit-delete-history-url (url-name)
  880. (let ((url-history (webkit-get-url-history url-name)))
  881. (when url-history
  882. (remhash url-name webkit-history-urls)
  883. (webkit-save-history-urls))))
  884.  
  885. (defun webkit-clean-history ()
  886. (setq webkit-history-urls nil)
  887. (webkit-save-history-urls))
  888.  
  889. (defun webkit-save-history-urls ()
  890. (with-current-buffer (find-file-noselect webkit-history-urls-path)
  891. (erase-buffer)
  892. (insert (prin1-to-string webkit-history-urls))
  893. (let ((delete-old-versions nil))
  894. (save-buffer 0))))
  895.  
  896. (defun webkit-restore-history-urls ()
  897. (if (file-exists-p webkit-history-urls-path)
  898. (setq webkit-history-urls
  899. (read
  900. (with-temp-buffer
  901. (insert-file-contents webkit-history-urls-path)
  902. (buffer-string)))))
  903.  
  904. ;; Init hash table if `webkit-history-urls' is nil.
  905. (unless webkit-history-urls
  906. (setq webkit-history-urls (make-hash-table :test 'equal)))
  907. )
  908.  
  909. (webkit-restore-history-urls)
  910.  
  911. (defun webkit-open-url (url)
  912. (interactive "sURL: ")
  913. (let* ((buffer (webkit-create-buffer url))
  914. (url-parts (split-string url "://"))
  915. )
  916. (unless (member (nth 0 url-parts) (list "http" "https" "ftp" "file"))
  917. (if (= (length url-parts) 1)
  918. (setq url (concat "http://" (nth 0 url-parts))))
  919. )
  920. (with-current-buffer buffer
  921. (let* ((window-allocation (webkit-get-window-allocation))
  922. (x (nth 0 window-allocation))
  923. (y (nth 1 window-allocation))
  924. (w (nth 2 window-allocation))
  925. (h (nth 3 window-allocation))
  926. (view-id (webkit-generate-id)))
  927. (epc:call-deferred pyepc-browser 'create_buffer (list buffer-id url w h))
  928. ))
  929. (switch-to-buffer buffer)
  930.  
  931. ;; Record browse history.
  932. (let* ((url-name (webkit-get-url-name url))
  933. (url-history (webkit-get-url-history url-name)))
  934. (if url-history
  935. (let ((url-number (car url-history))
  936. (url-title (cdr url-history)))
  937. (puthash url-name (list (+ url-number 1) url-title) webkit-history-urls))
  938. (puthash url-name (list 1 url) webkit-history-urls))
  939. (webkit-save-history-urls))
  940. ))
  941.  
  942. (defun webkit-monitor-window-change (&rest _)
  943. (let ((view-infos)
  944. (selected-buffer (window-buffer (selected-window))))
  945. (dolist (window (window-list))
  946. (let ((buffer (window-buffer window)))
  947. (with-current-buffer buffer
  948. (if (string= "webkit-mode" (format "%s" major-mode))
  949. (let* ((window-allocation (webkit-get-window-allocation window))
  950. (x (nth 0 window-allocation))
  951. (y (nth 1 window-allocation))
  952. (w (nth 2 window-allocation))
  953. (h (nth 3 window-allocation))
  954. )
  955. (add-to-list 'view-infos (list buffer-id x y w h))
  956. )))))
  957. (epc:call-deferred pyepc-browser 'update_views (list view-infos))
  958.  
  959. (with-current-buffer selected-buffer
  960. (if (string= "webkit-mode" (format "%s" major-mode))
  961. (let* ((window-allocation (webkit-get-window-allocation (selected-window)))
  962. (w (nth 2 window-allocation))
  963. (h (nth 3 window-allocation))
  964. )
  965. (epc:call-deferred pyepc-browser 'adjust_size (list buffer-id w h))
  966. )))
  967. ))
  968.  
  969. (defun webkit-monitor-buffer-kill ()
  970. (with-current-buffer (buffer-name)
  971. (if (string= "webkit-mode" (format "%s" major-mode))
  972. (progn
  973. (epc:call-deferred pyepc-browser 'remove_buffer (list buffer-id))
  974. (remhash buffer-id webkit-buffer-dict)))))
  975.  
  976. (defun webkit-focus-browser-view ()
  977. (interactive)
  978. (with-current-buffer (current-buffer)
  979. (if (string= "webkit-mode" (format "%s" major-mode))
  980. (let* ((window-allocation (webkit-get-window-allocation (get-buffer-window (current-buffer))))
  981. (x (nth 0 window-allocation))
  982. (y (nth 1 window-allocation))
  983. (w (nth 2 window-allocation))
  984. (h (nth 3 window-allocation))
  985. )
  986. (epc:call-deferred pyepc-browser 'focus_view (list buffer-id x y w h))
  987. (message "Focus view: %S" buffer-id)
  988. )
  989. )))
  990.  
  991. (defun webkit-toggle-console-info ()
  992. (interactive)
  993. (epc:call-deferred pyepc-browser 'toggle_console_info ()))
  994.  
  995. (defun webkit-enable-proxy ()
  996. (interactive)
  997. (setq webkit-enable-proxy-p t)
  998. (webkit-save-proxy-config)
  999. (message "Enable webkit proxy, reboot emacs effective."))
  1000.  
  1001. (defun webkit-disable-proxy ()
  1002. (interactive)
  1003. (setq webkit-enable-proxy-p nil)
  1004. (webkit-save-proxy-config)
  1005. (message "Disable webkit proxy, reboot emacs effective."))
  1006.  
  1007. (defadvice switch-to-buffer (after webkit-switch-to-buffer-advice activate)
  1008. (webkit-focus-browser-view))
  1009.  
  1010. (defadvice other-window (after webkit-other-window-advice activate)
  1011. (webkit-focus-browser-view))
  1012.  
  1013. (add-hook 'window-configuration-change-hook #'webkit-monitor-window-change)
  1014. (add-hook 'kill-buffer-hook #'webkit-monitor-buffer-kill)
  1015.  
  1016. (epc:define-method pyepc-browser
  1017. 'message
  1018. (lambda (&rest args) (message "%S" args)))
  1019.  
  1020. (epc:define-method pyepc-browser
  1021. 'open-url
  1022. (lambda (&rest args)
  1023. (webkit-open-url (nth 0 args))
  1024. ))
  1025.  
  1026. (epc:define-method pyepc-browser
  1027. 'change-buffer-title
  1028. (lambda (&rest args)
  1029. (webkit-change-buffer-title (nth 0 args) (nth 1 args))
  1030. ))
  1031.  
  1032. (epc:define-method pyepc-browser
  1033. 'focus-browser-view
  1034. (lambda (&rest args)
  1035. (webkit-focus-browser-view)
  1036. ))
  1037.  
  1038. (setq browse-url-browser-function (lambda (url flag) (webkit-open-url url)))
  1039.  
  1040. (provide 'webkit)
  1041.  
  1042. ;;; webkit.el ends here
  1043.  
  1044. #xutils.py
  1045. #! /usr/bin/env python
  1046. # -*- coding: utf-8 -*-
  1047.  
  1048. # Copyright (C) 2011 ~ 2014 Andy Stewart
  1049. #
  1050. # Author: Andy Stewart <lazycat.manatee@gmail.com>
  1051. # Maintainer: Andy Stewart <lazycat.manatee@gmail.com>
  1052. #
  1053. # This program is free software: you can redistribute it and/or modify
  1054. # it under the terms of the GNU General Public License as published by
  1055. # the Free Software Foundation, either version 3 of the License, or
  1056. # any later version.
  1057. #
  1058. # This program is distributed in the hope that it will be useful,
  1059. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  1060. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  1061. # GNU General Public License for more details.
  1062. #
  1063. # You should have received a copy of the GNU General Public License
  1064. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  1065.  
  1066. from PyQt5 import QtCore
  1067.  
  1068. xlib_display = None
  1069.  
  1070. def get_xlib_display():
  1071. global xlib_display
  1072.  
  1073. if xlib_display == None:
  1074. from Xlib import display
  1075. xlib_display = display.Display()
  1076.  
  1077. return xlib_display
  1078.  
  1079. def grab_focus(window_id):
  1080. global xlib_display
  1081.  
  1082. from Xlib import X
  1083. xwindow = xlib_display.create_resource_object("window", window_id)
  1084.  
  1085. xwindow.set_input_focus(X.RevertToNone, X.CurrentTime)
  1086. xwindow.configure(stack_mode=X.Above)
  1087.  
  1088. xlib_display.sync()
  1089.  
  1090. def get_parent_window_id(window_id):
  1091. xlib_display = get_xlib_display()
  1092.  
  1093. return xlib_display.create_resource_object("window", window_id).query_tree().parent.__window__()
  1094.  
  1095. class ActiveWindowWatcher(QtCore.QThread):
  1096.  
  1097. activeWindowChanged = QtCore.pyqtSignal(int)
  1098.  
  1099. def __init__(self):
  1100. super(ActiveWindowWatcher, self).__init__()
  1101.  
  1102. xlib_display = get_xlib_display()
  1103.  
  1104. from Xlib import X
  1105. self.root = xlib_display.screen().root
  1106. self.root.change_attributes(event_mask=(X.PropertyChangeMask))
  1107. self.ACTIVE = xlib_display.intern_atom("_NET_ACTIVE_WINDOW")
  1108. xlib_display.flush()
  1109.  
  1110. self.active_window = self.root.get_full_property(self.ACTIVE, 0).value[0]
  1111. self.do_active_window()
  1112.  
  1113. def do_active_window(self):
  1114. active = self.root.get_full_property(self.ACTIVE, 0).value[0]
  1115. if active != self.active_window:
  1116. self.active_window = active
  1117.  
  1118. self.activeWindowChanged.emit(self.active_window.__int__())
  1119.  
  1120. def run(self):
  1121. xlib_display = get_xlib_display()
  1122.  
  1123. import time
  1124. from Xlib import X
  1125.  
  1126. while 1:
  1127. while xlib_display.pending_events():
  1128. e = xlib_display.next_event()
  1129. if e.type == X.PropertyNotify:
  1130. self.do_active_window()
  1131. time.sleep(0.1)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement