Home | History | Annotate | Download | only in host
      1 #!/usr/bin/env python
      2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 # A simple native messaging host. Shows a Tkinter dialog with incoming messages
      7 # that also allows to send message back to the webapp.
      8 
      9 import struct
     10 import sys
     11 import threading
     12 import Queue
     13 
     14 try:
     15   import Tkinter
     16   import tkMessageBox
     17 except ImportError:
     18   Tkinter = None
     19 
     20 # Helper function that sends a message to the webapp.
     21 def send_message(message):
     22    # Write message size.
     23   sys.stdout.write(struct.pack('I', len(message)))
     24   # Write the message itself.
     25   sys.stdout.write(message)
     26   sys.stdout.flush()
     27 
     28 # Thread that reads messages from the webapp.
     29 def read_thread_func(queue):
     30   message_number = 0
     31   while 1:
     32     # Read the message length (first 4 bytes).
     33     text_length_bytes = sys.stdin.read(4)
     34 
     35     if len(text_length_bytes) == 0:
     36       if queue:
     37         queue.put(None)
     38       sys.exit(0)
     39 
     40     # Unpack message length as 4 byte integer.
     41     text_length = struct.unpack('i', text_length_bytes)[0]
     42 
     43     # Read the text (JSON object) of the message.
     44     text = sys.stdin.read(text_length).decode('utf-8')
     45 
     46     if queue:
     47       queue.put(text)
     48     else:
     49       # In headless mode just send an echo message back.
     50       send_message('{"echo": %s}' % text)
     51 
     52 if Tkinter:
     53   class NativeMessagingWindow(Tkinter.Frame):
     54     def __init__(self, queue):
     55       self.queue = queue
     56 
     57       Tkinter.Frame.__init__(self)
     58       self.pack()
     59 
     60       self.text = Tkinter.Text(self)
     61       self.text.grid(row=0, column=0, padx=10, pady=10, columnspan=2)
     62       self.text.config(state=Tkinter.DISABLED, height=10, width=40)
     63 
     64       self.messageContent = Tkinter.StringVar()
     65       self.sendEntry = Tkinter.Entry(self, textvariable=self.messageContent)
     66       self.sendEntry.grid(row=1, column=0, padx=10, pady=10)
     67 
     68       self.sendButton = Tkinter.Button(self, text="Send", command=self.onSend)
     69       self.sendButton.grid(row=1, column=1, padx=10, pady=10)
     70 
     71       self.after(100, self.processMessages)
     72 
     73     def processMessages(self):
     74       while not self.queue.empty():
     75         message = self.queue.get_nowait()
     76         if message == None:
     77           self.quit()
     78           return
     79         self.log("Received %s" % message)
     80 
     81       self.after(100, self.processMessages)
     82 
     83     def onSend(self):
     84       text = '{"text": "' + self.messageContent.get() + '"}'
     85       self.log('Sending %s' % text)
     86       try:
     87         send_message(text)
     88       except IOError:
     89         tkMessageBox.showinfo('Native Messaging Example',
     90                               'Failed to send message.')
     91         sys.exit(1)
     92 
     93     def log(self, message):
     94       self.text.config(state=Tkinter.NORMAL)
     95       self.text.insert(Tkinter.END, message + "\n")
     96       self.text.config(state=Tkinter.DISABLED)
     97 
     98 
     99 def Main():
    100   if not Tkinter:
    101     send_message('"Tkinter python module wasn\'t found. Running in headless ' +
    102                  'mode. Please consider installing Tkinter."')
    103     read_thread_func(None)
    104     sys.exit(0)
    105 
    106   queue = Queue.Queue()
    107 
    108   main_window = NativeMessagingWindow(queue)
    109   main_window.master.title('Native Messaging Example')
    110 
    111   thread = threading.Thread(target=read_thread_func, args=(queue,))
    112   thread.daemon = True
    113   thread.start()
    114 
    115   main_window.mainloop()
    116 
    117   sys.exit(0)
    118 
    119 
    120 if __name__ == '__main__':
    121   Main()
    122