diff --git a/onionshare_gui/gui.py b/onionshare_gui/gui.py
deleted file mode 100644
index 270678c2..00000000
--- a/onionshare_gui/gui.py
+++ /dev/null
@@ -1,72 +0,0 @@
-#!/usr/bin/env python
-
-import Tkinter as tk, tkFont, tkFileDialog
-import sys, os, time
-import onionshare
-from Queue import Queue, Empty
-from threading import Thread
-
-class OnionShareGUI(object):
- def __init__(self):
- self.root = tk.Tk()
-
- # prepare GUI
- self.root.title('OnionShare')
- self.root.resizable(0, 0)
- self.create_widgets()
- self.root.grid()
-
- # select file
- if len(sys.argv) >= 2:
- self.filename = sys.argv[1]
- else:
- self.filename = tkFileDialog.askopenfilename(title="Choose a file to share", parent=self.root)
- self.basename = os.path.basename(self.filename)
- self.root.title('OnionShare - {0}'.format(self.basename))
-
- # todo: start onionshare here, and display web server logs in update() method
- # this might be helpful: https://stackoverflow.com/questions/375427/non-blocking-read-on-a-subprocess-pipe-in-python
-
- # update regularly
- self.update()
-
- def create_widgets(self):
- self.pad = 10
- sys12 = tkFont.Font(family="system", size=12)
- sys20 = tkFont.Font(family="system", size=20, weight="bold")
-
- # url
- self.url_labelframe = tk.LabelFrame(text="Send this URL to your friend")
- self.url_labelframe.pack()
- self.url_text = tk.Text(self.url_labelframe, width=31, height=2, font=sys20)
- self.url_text.config(state=tk.DISABLED)
- self.url_text.pack(padx=self.pad, pady=self.pad)
- self.url_labelframe.grid(padx=self.pad, pady=self.pad)
-
- # logs
- self.logs_labelframe = tk.LabelFrame(text="Server logs")
- self.logs_labelframe.pack()
- self.logs_text = tk.Text(self.logs_labelframe, width=70, height=10, font=sys12)
- self.logs_text.insert(tk.INSERT, "")
- self.logs_text.config(state=tk.DISABLED)
- self.logs_text.pack(padx=self.pad, pady=self.pad)
- self.logs_labelframe.grid(padx=self.pad, pady=self.pad)
-
- # quit button
- self.quit_button = tk.Button(self.root, text='Quit', command=self.root.quit)
- self.quit_button.grid(padx=self.pad, pady=self.pad)
-
- def update(self):
- self.root.after(500, self.update)
-
- def enqueue_output(self, out, queue):
- for line in iter(out.readline, b''):
- queue.put(line)
- out.close()
-
-def main():
- app = OnionShareGUI()
- app.root.mainloop()
-
-if __name__ == '__main__':
- main()
diff --git a/onionshare_gui/index.html b/onionshare_gui/index.html
new file mode 100644
index 00000000..e7b5b84d
--- /dev/null
+++ b/onionshare_gui/index.html
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+☠ filename.zip
+
+
+
diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py
new file mode 100644
index 00000000..098a33ad
--- /dev/null
+++ b/onionshare_gui/onionshare_gui.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+
+import onionshare, webgui
+import signal, os, time, json
+
+class Global(object):
+ quit = False
+ @classmethod
+ def set_quit(cls, *args, **kwargs):
+ cls.quit = True
+
+def main():
+ if not webgui.select_file():
+ return
+
+ webgui.start_gtk_thread()
+ browser, web_recv, web_send = webgui.sync_gtk_msg(webgui.launch_browser)(quit_function=Global.set_quit)
+
+ last_second = time.time()
+ uptime_seconds = 1
+ clicks = 0
+ while not Global.quit:
+
+ current_time = time.time()
+ again = False
+ msg = web_recv()
+ if msg:
+ msg = json.loads(msg)
+ again = True
+
+ if msg == "got-a-click":
+ clicks += 1
+ web_send('document.getElementById("messages").innerHTML = %s' %
+ to_json('%d clicks so far' % clicks))
+ # If you are using jQuery, you can do this instead:
+ # web_send('$("#messages").text(%s)' %
+ # to_json('%d clicks so far' % clicks))
+
+ if current_time - last_second >= 1.0:
+ web_send('document.getElementById("uptime-value").innerHTML = %s' %
+ json.dumps('%d' % uptime_seconds))
+ # If you are using jQuery, you can do this instead:
+ # web_send('$("#uptime-value").text(%s)'
+ # % to_json('%d' % uptime_seconds))
+ uptime_seconds += 1
+ last_second += 1.0
+
+
+ if again:
+ pass
+ else:
+ time.sleep(0.1)
+
+def my_quit_wrapper(fun):
+ signal.signal(signal.SIGINT, Global.set_quit)
+ def fun2(*args, **kwargs):
+ try:
+ x = fun(*args, **kwargs) # equivalent to "apply"
+ finally:
+ kill_gtk_thread()
+ Global.set_quit()
+ return x
+ return fun2
+
+if __name__ == '__main__':
+ main()
diff --git a/onionshare_gui/webgui.py b/onionshare_gui/webgui.py
new file mode 100644
index 00000000..c412da26
--- /dev/null
+++ b/onionshare_gui/webgui.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env python
+
+import time, Queue, thread, gtk, gobject, os, sys, webkit
+
+def select_file():
+ global filename, basename
+
+ # was a filename passed in as an argument?
+ if len(sys.argv) >= 2:
+ filename = sys.argv[1]
+ basename = os.path.basename(filename)
+ return True
+
+ # choose a file
+ canceled = False
+ chooser = gtk.FileChooserDialog(
+ title="Choose a file to share",
+ action=gtk.FILE_CHOOSER_ACTION_OPEN,
+ buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK))
+ response = chooser.run()
+ if response == gtk.RESPONSE_OK:
+ filename = chooser.get_filename()
+ basename = os.path.basename(filename)
+ elif response == gtk.RESPONSE_CANCEL:
+ canceled = True
+ chooser.destroy()
+
+ return not canceled
+
+def async_gtk_msg(fun):
+ def worker((function, args, kwargs)):
+ apply(function, args, kwargs)
+
+ def fun2(*args, **kwargs):
+ gobject.idle_add(worker, (fun, args, kwargs))
+
+ return fun2
+
+def sync_gtk_msg(fun):
+ class NoResult: pass
+
+ def worker((R, function, args, kwargs)):
+ R.result = apply(function, args, kwargs)
+
+ def fun2(*args, **kwargs):
+ class R: result = NoResult
+ gobject.idle_add(callable=worker, user_data=(R, fun, args, kwargs))
+ while R.result is NoResult: time.sleep(0.01)
+ return R.result
+
+ return fun2
+
+def launch_browser(quit_function=None, echo=True):
+ window = gtk.Window()
+ browser = webkit.WebView()
+
+ box = gtk.VBox(homogeneous=False, spacing=0)
+ window.add(box)
+
+ if quit_function is not None:
+ # file > quit menu
+ file_menu = gtk.Menu()
+ quit_item = gtk.MenuItem('Quit')
+ accel_group = gtk.AccelGroup()
+ quit_item.add_accelerator('activate', accel_group, ord('Q'), gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE)
+ window.add_accel_group(accel_group)
+ file_menu.append(quit_item)
+ quit_item.connect('activate', quit_function)
+ quit_item.show()
+ menu_bar = gtk.MenuBar()
+ menu_bar.show()
+ file_item = gtk.MenuItem('File')
+ file_item.show()
+ file_item.set_submenu(file_menu)
+ menu_bar.append(file_item)
+
+ box.pack_start(menu_bar, expand=False, fill=True, padding=0)
+
+ window.connect('destroy', quit_function)
+
+ box.pack_start(browser, expand=True, fill=True, padding=0)
+
+ window.set_default_size(400, 400)
+ window.show_all()
+
+ message_queue = Queue.Queue()
+
+ def title_changed(title):
+ if title != 'null': message_queue.put(title)
+
+ def callback_wrapper(widget, frame, title): callback(title)
+ browser.connect('title-changed', callback_wrapper)
+
+ browser.open('file://'+os.getcwd()+'/index.html')
+
+ def web_recv():
+ if message_queue.empty():
+ return None
+ else:
+ msg = message_queue.get()
+ if echo: print '>>>', msg
+ return msg
+
+ def web_send(msg):
+ if echo: print '<<<', msg
+ async_gtk_msg(browser.execute_script)(msg)
+
+ return browser, web_recv, web_send
+
+
+def start_gtk_thread():
+ # Start GTK in its own thread:
+ gtk.gdk.threads_init()
+ thread.start_new_thread(gtk.main, ())
+
+def kill_gtk_thread():
+ async_gtk_msg(gtk.main_quit)()
+
+def main():
+ if not select_file():
+ return
+
+ launch_browser()