We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Implement a server-side GLIR cache
This is backend-independent. The GL context needs to store a list of (almost) all GLIR commands emitted since initialization. Unnecessary GLIR commands (old commands superseded by new commands) are automatically discarded (pruning), so that this list should never be very long.
- [ ] implement the cache (easy)
- [ ] dynamically prune old GLIR commands that are superseded by new ones: `DRAW`, `UNIFORM`, `FUNC` (slightly less straightforward)
- completely get rid of `on_initialize()` (can be done in another PR)
- fix the `ipynb_webgl` backend so that the latest state is automatically reinitialized when the context is lost (can be done in another PR)


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `vispy/app/backends/_ipynb_webgl.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 # Copyright (c) 2014, Vispy Development Team.
3 # Distributed under the (new) BSD License. See LICENSE.txt for more info.
4 
5 """
6 Vispy backend for the IPython notebook (WebGL approach).
7 """
8 
9 from __future__ import division
10 
11 from ..base import (BaseApplicationBackend, BaseCanvasBackend,
12                     BaseTimerBackend)
13 from ._ipynb_util import create_glir_message
14 from ...util import logger, keys
15 from ...ext import six
16 from vispy.gloo.glir import BaseGlirParser
17 
18 # Import for displaying Javascript on notebook
19 import os.path as op
20 
21 # -------------------------------------------------------------------- init ---
22 
23 capability = dict(  # things that can be set by the backend
24     title=True,  # But it only applies to the dummy window :P
25     size=True,  # We cannot possibly say we dont, because Canvas always sets it
26     position=True,  # Dito
27     show=True,
28     vsync=False,
29     resizable=True,
30     decorate=False,
31     fullscreen=True,
32     context=True,
33     multi_window=False,
34     scroll=True,
35     parent=False,
36 )
37 
38 
39 # Init dummy objects needed to import this module withour errors.
40 # These are all overwritten with imports from IPython (on success)
41 DOMWidget = object
42 Unicode = Int = Float = Bool = lambda *args, **kwargs: None
43 
44 # Try importing IPython
45 try:
46     import tornado
47     import IPython
48     IPYTHON_MAJOR_VERSION = IPython.version_info[0]
49     if IPYTHON_MAJOR_VERSION < 2:
50         raise RuntimeError('ipynb_webgl backend requires IPython >= 2.0')
51     from IPython.html.widgets import DOMWidget
52     from IPython.utils.traitlets import Unicode, Int
53     from IPython.display import display, Javascript
54     from IPython.html.nbextensions import install_nbextension
55 except Exception as exp:
56     # raise ImportError("The WebGL backend requires IPython >= 2.0")
57     available, testable, why_not, which = False, False, str(exp), None
58 else:
59     available, testable, why_not, which = True, False, None, None
60 
61 
62 # ------------------------------------------------------------- application ---
63 def _prepare_js():
64     pkgdir = op.dirname(__file__)
65     jsdir = op.join(pkgdir, '../../html/static/js/')
66     # Make sure the JS files are installed to user directory (new argument
67     # in IPython 3.0).
68     if IPYTHON_MAJOR_VERSION >= 3:
69         kwargs = {'user': True}
70     else:
71         kwargs = {}
72     install_nbextension([op.join(jsdir, 'vispy.min.js'),
73                          op.join(jsdir, 'jquery.mousewheel.min.js')],
74                         **kwargs)
75     backend_path = op.join(jsdir, 'webgl-backend.js')
76     with open(backend_path, 'r') as f:
77         script = f.read()
78     display(Javascript(script))
79 
80 
81 class ApplicationBackend(BaseApplicationBackend):
82 
83     def __init__(self):
84         BaseApplicationBackend.__init__(self)
85         _prepare_js()
86 
87     def _vispy_reuse(self):
88         _prepare_js()
89 
90     def _vispy_get_backend_name(self):
91         return 'ipynb_webgl'
92 
93     def _vispy_process_events(self):
94         # TODO: may be implemented later.
95         raise NotImplementedError()
96 
97     def _vispy_run(self):
98         pass
99 
100     def _vispy_quit(self):
101         pass
102 
103     def _vispy_get_native_app(self):
104         return self
105 
106 
107 # ------------------------------------------------------------------ canvas ---
108 class WebGLGlirParser(BaseGlirParser):
109     def __init__(self, widget):
110         self._widget = widget
111 
112     def is_remote(self):
113         return True
114 
115     def convert_shaders(self):
116         return 'es2'
117 
118     def parse(self, commands):
119         self._widget.send_glir_commands(commands)
120 
121 
122 class CanvasBackend(BaseCanvasBackend):
123     # args are for BaseCanvasBackend, kwargs are for us.
124     def __init__(self, *args, **kwargs):
125         BaseCanvasBackend.__init__(self, *args)
126         # Maybe to ensure that exactly all arguments are passed?
127         title, size, position, show, vsync, resize, dec, fs, parent, context, \
128             = self._process_backend_kwargs(kwargs)
129         self._context = context
130 
131         # TODO: do something with context.config
132         # Take the context.
133         context.shared.add_ref('webgl', self)
134         if context.shared.ref is self:
135             pass  # ok
136         else:
137             raise RuntimeError("WebGL doesn't yet support context sharing.")
138 
139         self._create_widget(size=size)
140 
141     def _create_widget(self, size=None):
142         self._widget = VispyWidget(self._gen_event, size=size)
143         # Set glir parser on context and context.shared
144         context = self._vispy_canvas.context
145         context.shared.parser = WebGLGlirParser(self._widget)
146 
147     def _reinit_widget(self):
148         self._vispy_canvas.set_current()
149 
150         self._vispy_canvas.events.initialize()
151         self._vispy_canvas.events.resize(size=(self._widget.width,
152                                                self._widget.height))
153         self._vispy_canvas.events.draw()
154 
155     def _vispy_warmup(self):
156         pass
157 
158     # Uncommenting these makes the backend crash.
159     def _vispy_set_current(self):
160         pass
161 
162     def _vispy_swap_buffers(self):
163         pass
164 
165     def _vispy_set_title(self, title):
166         raise NotImplementedError()
167 
168     def _vispy_get_fullscreen(self):
169         # We don't want error messages to show up when the user presses
170         # F11 to fullscreen the browser.
171         pass
172 
173     def _vispy_set_fullscreen(self, fullscreen):
174         # We don't want error messages to show up when the user presses
175         # F11 to fullscreen the browser.
176         pass
177 
178     def _vispy_get_size(self):
179         return (self._widget.width, self._widget.height)
180 
181     def _vispy_set_size(self, w, h):
182         self._widget.width = w
183         self._widget.height = h
184 
185     def _vispy_get_position(self):
186         raise NotImplementedError()
187 
188     def _vispy_set_position(self, x, y):
189         logger.warning('IPython notebook canvas cannot be repositioned.')
190 
191     def _vispy_set_visible(self, visible):
192         if not visible:
193             logger.warning('IPython notebook canvas cannot be hidden.')
194         else:
195             display(self._widget)
196             self._reinit_widget()
197 
198     def _vispy_update(self):
199         ioloop = tornado.ioloop.IOLoop.current()
200         ioloop.add_callback(self._draw_event)
201 
202     def _draw_event(self):
203         self._vispy_canvas.set_current()
204         self._vispy_canvas.events.draw()
205 
206     def _vispy_close(self):
207         raise NotImplementedError()
208 
209     def _vispy_mouse_release(self, **kwds):
210         # HACK: override this method from the base canvas in order to
211         # avoid breaking other backends.
212         kwds.update(self._vispy_mouse_data)
213         ev = self._vispy_canvas.events.mouse_release(**kwds)
214         if ev is None:
215             return
216         self._vispy_mouse_data['press_event'] = None
217         # TODO: this is a bit ugly, need to improve mouse button handling in
218         # app
219         ev._button = None
220         self._vispy_mouse_data['buttons'] = []
221         self._vispy_mouse_data['last_event'] = ev
222         return ev
223 
224     # Generate vispy events according to upcoming JS events
225     _modifiers_map = {
226         'ctrl': keys.CONTROL,
227         'shift': keys.SHIFT,
228         'alt': keys.ALT,
229     }
230 
231     def _gen_event(self, ev):
232         if self._vispy_canvas is None:
233             return
234         event_type = ev['type']
235         key_code = ev.get('key_code', None)
236         if key_code is None:
237             key, key_text = None, None
238         else:
239             if hasattr(keys, key_code):
240                 key = getattr(keys, key_code)
241             else:
242                 key = keys.Key(key_code)
243             # Generate the key text to pass to the event handler.
244             if key_code == 'SPACE':
245                 key_text = ' '
246             else:
247                 key_text = six.text_type(key_code)
248         # Process modifiers.
249         modifiers = ev.get('modifiers', None)
250         if modifiers:
251             modifiers = tuple([self._modifiers_map[modifier]
252                                for modifier in modifiers
253                                if modifier in self._modifiers_map])
254         if event_type == "mouse_move":
255             self._vispy_mouse_move(native=ev,
256                                    button=ev["button"],
257                                    pos=ev["pos"],
258                                    modifiers=modifiers,
259                                    )
260         elif event_type == "mouse_press":
261             self._vispy_mouse_press(native=ev,
262                                     pos=ev["pos"],
263                                     button=ev["button"],
264                                     modifiers=modifiers,
265                                     )
266         elif event_type == "mouse_release":
267             self._vispy_mouse_release(native=ev,
268                                       pos=ev["pos"],
269                                       button=ev["button"],
270                                       modifiers=modifiers,
271                                       )
272         elif event_type == "mouse_wheel":
273             self._vispy_canvas.events.mouse_wheel(native=ev,
274                                                   delta=ev["delta"],
275                                                   pos=ev["pos"],
276                                                   button=ev["button"],
277                                                   modifiers=modifiers,
278                                                   )
279         elif event_type == "key_press":
280             self._vispy_canvas.events.key_press(native=ev,
281                                                 key=key,
282                                                 text=key_text,
283                                                 modifiers=modifiers,
284                                                 )
285         elif event_type == "key_release":
286             self._vispy_canvas.events.key_release(native=ev,
287                                                   key=key,
288                                                   text=key_text,
289                                                   modifiers=modifiers,
290                                                   )
291         elif event_type == "resize":
292             self._vispy_canvas.events.resize(native=ev,
293                                              size=ev["size"])
294         elif event_type == "paint":
295             self._vispy_canvas.events.draw()
296 
297 
298 # ------------------------------------------------------------------- Timer ---
299 class TimerBackend(BaseTimerBackend):
300     def __init__(self, *args, **kwargs):
301         super(TimerBackend, self).__init__(*args, **kwargs)
302         self._timer = tornado.ioloop.PeriodicCallback(
303             self._vispy_timer._timeout,
304             1000)
305 
306     def _vispy_start(self, interval):
307         self._timer.callback_time = interval * 1000
308         self._timer.start()
309 
310     def _vispy_stop(self):
311         self._timer.stop()
312 
313 
314 # ---------------------------------------------------------- IPython Widget ---
315 class VispyWidget(DOMWidget):
316     _view_name = Unicode("VispyView", sync=True)
317 
318     width = Int(sync=True)
319     height = Int(sync=True)
320 
321     def __init__(self, gen_event, **kwargs):
322         super(VispyWidget, self).__init__(**kwargs)
323         w, h = kwargs.get('size', (500, 200))
324         self.width = w
325         self.height = h
326         self.gen_event = gen_event
327         self.on_msg(self.events_received)
328 
329     def events_received(self, _, msg):
330         if msg['msg_type'] == 'events':
331             events = msg['contents']
332             for ev in events:
333                 self.gen_event(ev)
334 
335     def send_glir_commands(self, commands):
336         # TODO: check whether binary websocket is available (ipython >= 3)
337         # Until IPython 3.0 is released, use base64.
338         array_serialization = 'base64'
339         # array_serialization = 'binary'
340         if array_serialization == 'base64':
341             msg = create_glir_message(commands, 'base64')
342             msg['array_serialization'] = 'base64'
343             self.send(msg)
344         elif array_serialization == 'binary':
345             msg = create_glir_message(commands, 'binary')
346             msg['array_serialization'] = 'binary'
347             # Remove the buffers from the JSON message: they will be sent
348             # independently via binary WebSocket.
349             buffers = msg.pop('buffers')
350             self.comm.send({"method": "custom", "content": msg},
351                            buffers=buffers)
352
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/vispy/app/backends/_ipynb_webgl.py b/vispy/app/backends/_ipynb_webgl.py
--- a/vispy/app/backends/_ipynb_webgl.py
+++ b/vispy/app/backends/_ipynb_webgl.py
@@ -14,6 +14,7 @@
 from ...util import logger, keys
 from ...ext import six
 from vispy.gloo.glir import BaseGlirParser
+from vispy.app import Timer
 
 # Import for displaying Javascript on notebook
 import os.path as op
@@ -60,7 +61,7 @@
 
 
 # ------------------------------------------------------------- application ---
-def _prepare_js():
+def _prepare_js(force=False):
     pkgdir = op.dirname(__file__)
     jsdir = op.join(pkgdir, '../../html/static/js/')
     # Make sure the JS files are installed to user directory (new argument
@@ -69,8 +70,7 @@
         kwargs = {'user': True}
     else:
         kwargs = {}
-    install_nbextension([op.join(jsdir, 'vispy.min.js'),
-                         op.join(jsdir, 'jquery.mousewheel.min.js')],
+    install_nbextension(op.join(jsdir, 'vispy.min.js'), overwrite=force,
                         **kwargs)
     backend_path = op.join(jsdir, 'webgl-backend.js')
     with open(backend_path, 'r') as f:
@@ -139,7 +139,7 @@
         self._create_widget(size=size)
 
     def _create_widget(self, size=None):
-        self._widget = VispyWidget(self._gen_event, size=size)
+        self._widget = VispyWidget(self, size=size)
         # Set glir parser on context and context.shared
         context = self._vispy_canvas.context
         context.shared.parser = WebGLGlirParser(self._widget)
@@ -312,18 +312,32 @@
 
 
 # ---------------------------------------------------------- IPython Widget ---
+def _stop_timers(canvas):
+    """Stop all timers in a canvas."""
+    for attr in dir(canvas):
+        try:
+            attr_obj = getattr(canvas, attr)
+        except NotImplementedError:
+            # This try/except is needed because canvas.position raises
+            # an error (it is not implemented in this backend).
+            attr_obj = None
+        if isinstance(attr_obj, Timer):
+            attr_obj.stop()
+
+
 class VispyWidget(DOMWidget):
     _view_name = Unicode("VispyView", sync=True)
 
     width = Int(sync=True)
     height = Int(sync=True)
 
-    def __init__(self, gen_event, **kwargs):
+    def __init__(self, canvas_backend, **kwargs):
         super(VispyWidget, self).__init__(**kwargs)
         w, h = kwargs.get('size', (500, 200))
         self.width = w
         self.height = h
-        self.gen_event = gen_event
+        self.canvas_backend = canvas_backend
+        self.gen_event = canvas_backend._gen_event
         self.on_msg(self.events_received)
 
     def events_received(self, _, msg):
@@ -331,6 +345,10 @@
             events = msg['contents']
             for ev in events:
                 self.gen_event(ev)
+        elif msg['msg_type'] == 'status':
+            if msg['contents'] == 'removed':
+                # Stop all timers associated to the widget.
+                _stop_timers(self.canvas_backend._vispy_canvas)
 
     def send_glir_commands(self, commands):
         # TODO: check whether binary websocket is available (ipython >= 3)

{"golden_diff": "diff --git a/vispy/app/backends/_ipynb_webgl.py b/vispy/app/backends/_ipynb_webgl.py\n--- a/vispy/app/backends/_ipynb_webgl.py\n+++ b/vispy/app/backends/_ipynb_webgl.py\n@@ -14,6 +14,7 @@\n from ...util import logger, keys\n from ...ext import six\n from vispy.gloo.glir import BaseGlirParser\n+from vispy.app import Timer\n \n # Import for displaying Javascript on notebook\n import os.path as op\n@@ -60,7 +61,7 @@\n \n \n # ------------------------------------------------------------- application ---\n-def _prepare_js():\n+def _prepare_js(force=False):\n     pkgdir = op.dirname(__file__)\n     jsdir = op.join(pkgdir, '../../html/static/js/')\n     # Make sure the JS files are installed to user directory (new argument\n@@ -69,8 +70,7 @@\n         kwargs = {'user': True}\n     else:\n         kwargs = {}\n-    install_nbextension([op.join(jsdir, 'vispy.min.js'),\n-                         op.join(jsdir, 'jquery.mousewheel.min.js')],\n+    install_nbextension(op.join(jsdir, 'vispy.min.js'), overwrite=force,\n                         **kwargs)\n     backend_path = op.join(jsdir, 'webgl-backend.js')\n     with open(backend_path, 'r') as f:\n@@ -139,7 +139,7 @@\n         self._create_widget(size=size)\n \n     def _create_widget(self, size=None):\n-        self._widget = VispyWidget(self._gen_event, size=size)\n+        self._widget = VispyWidget(self, size=size)\n         # Set glir parser on context and context.shared\n         context = self._vispy_canvas.context\n         context.shared.parser = WebGLGlirParser(self._widget)\n@@ -312,18 +312,32 @@\n \n \n # ---------------------------------------------------------- IPython Widget ---\n+def _stop_timers(canvas):\n+    \"\"\"Stop all timers in a canvas.\"\"\"\n+    for attr in dir(canvas):\n+        try:\n+            attr_obj = getattr(canvas, attr)\n+        except NotImplementedError:\n+            # This try/except is needed because canvas.position raises\n+            # an error (it is not implemented in this backend).\n+            attr_obj = None\n+        if isinstance(attr_obj, Timer):\n+            attr_obj.stop()\n+\n+\n class VispyWidget(DOMWidget):\n     _view_name = Unicode(\"VispyView\", sync=True)\n \n     width = Int(sync=True)\n     height = Int(sync=True)\n \n-    def __init__(self, gen_event, **kwargs):\n+    def __init__(self, canvas_backend, **kwargs):\n         super(VispyWidget, self).__init__(**kwargs)\n         w, h = kwargs.get('size', (500, 200))\n         self.width = w\n         self.height = h\n-        self.gen_event = gen_event\n+        self.canvas_backend = canvas_backend\n+        self.gen_event = canvas_backend._gen_event\n         self.on_msg(self.events_received)\n \n     def events_received(self, _, msg):\n@@ -331,6 +345,10 @@\n             events = msg['contents']\n             for ev in events:\n                 self.gen_event(ev)\n+        elif msg['msg_type'] == 'status':\n+            if msg['contents'] == 'removed':\n+                # Stop all timers associated to the widget.\n+                _stop_timers(self.canvas_backend._vispy_canvas)\n \n     def send_glir_commands(self, commands):\n         # TODO: check whether binary websocket is available (ipython >= 3)\n", "issue": "Implement a server-side GLIR cache\nThis is backend-independent. The GL context needs to store a list of (almost) all GLIR commands emitted since initialization. Unnecessary GLIR commands (old commands superseded by new commands) are automatically discarded (pruning), so that this list should never be very long.\n- [ ] implement the cache (easy)\n- [ ] dynamically prune old GLIR commands that are superseded by new ones: `DRAW`, `UNIFORM`, `FUNC` (slightly less straightforward)\n- completely get rid of `on_initialize()` (can be done in another PR)\n- fix the `ipynb_webgl` backend so that the latest state is automatically reinitialized when the context is lost (can be done in another PR)\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n# Copyright (c) 2014, Vispy Development Team.\n# Distributed under the (new) BSD License. See LICENSE.txt for more info.\n\n\"\"\"\nVispy backend for the IPython notebook (WebGL approach).\n\"\"\"\n\nfrom __future__ import division\n\nfrom ..base import (BaseApplicationBackend, BaseCanvasBackend,\n                    BaseTimerBackend)\nfrom ._ipynb_util import create_glir_message\nfrom ...util import logger, keys\nfrom ...ext import six\nfrom vispy.gloo.glir import BaseGlirParser\n\n# Import for displaying Javascript on notebook\nimport os.path as op\n\n# -------------------------------------------------------------------- init ---\n\ncapability = dict(  # things that can be set by the backend\n    title=True,  # But it only applies to the dummy window :P\n    size=True,  # We cannot possibly say we dont, because Canvas always sets it\n    position=True,  # Dito\n    show=True,\n    vsync=False,\n    resizable=True,\n    decorate=False,\n    fullscreen=True,\n    context=True,\n    multi_window=False,\n    scroll=True,\n    parent=False,\n)\n\n\n# Init dummy objects needed to import this module withour errors.\n# These are all overwritten with imports from IPython (on success)\nDOMWidget = object\nUnicode = Int = Float = Bool = lambda *args, **kwargs: None\n\n# Try importing IPython\ntry:\n    import tornado\n    import IPython\n    IPYTHON_MAJOR_VERSION = IPython.version_info[0]\n    if IPYTHON_MAJOR_VERSION < 2:\n        raise RuntimeError('ipynb_webgl backend requires IPython >= 2.0')\n    from IPython.html.widgets import DOMWidget\n    from IPython.utils.traitlets import Unicode, Int\n    from IPython.display import display, Javascript\n    from IPython.html.nbextensions import install_nbextension\nexcept Exception as exp:\n    # raise ImportError(\"The WebGL backend requires IPython >= 2.0\")\n    available, testable, why_not, which = False, False, str(exp), None\nelse:\n    available, testable, why_not, which = True, False, None, None\n\n\n# ------------------------------------------------------------- application ---\ndef _prepare_js():\n    pkgdir = op.dirname(__file__)\n    jsdir = op.join(pkgdir, '../../html/static/js/')\n    # Make sure the JS files are installed to user directory (new argument\n    # in IPython 3.0).\n    if IPYTHON_MAJOR_VERSION >= 3:\n        kwargs = {'user': True}\n    else:\n        kwargs = {}\n    install_nbextension([op.join(jsdir, 'vispy.min.js'),\n                         op.join(jsdir, 'jquery.mousewheel.min.js')],\n                        **kwargs)\n    backend_path = op.join(jsdir, 'webgl-backend.js')\n    with open(backend_path, 'r') as f:\n        script = f.read()\n    display(Javascript(script))\n\n\nclass ApplicationBackend(BaseApplicationBackend):\n\n    def __init__(self):\n        BaseApplicationBackend.__init__(self)\n        _prepare_js()\n\n    def _vispy_reuse(self):\n        _prepare_js()\n\n    def _vispy_get_backend_name(self):\n        return 'ipynb_webgl'\n\n    def _vispy_process_events(self):\n        # TODO: may be implemented later.\n        raise NotImplementedError()\n\n    def _vispy_run(self):\n        pass\n\n    def _vispy_quit(self):\n        pass\n\n    def _vispy_get_native_app(self):\n        return self\n\n\n# ------------------------------------------------------------------ canvas ---\nclass WebGLGlirParser(BaseGlirParser):\n    def __init__(self, widget):\n        self._widget = widget\n\n    def is_remote(self):\n        return True\n\n    def convert_shaders(self):\n        return 'es2'\n\n    def parse(self, commands):\n        self._widget.send_glir_commands(commands)\n\n\nclass CanvasBackend(BaseCanvasBackend):\n    # args are for BaseCanvasBackend, kwargs are for us.\n    def __init__(self, *args, **kwargs):\n        BaseCanvasBackend.__init__(self, *args)\n        # Maybe to ensure that exactly all arguments are passed?\n        title, size, position, show, vsync, resize, dec, fs, parent, context, \\\n            = self._process_backend_kwargs(kwargs)\n        self._context = context\n\n        # TODO: do something with context.config\n        # Take the context.\n        context.shared.add_ref('webgl', self)\n        if context.shared.ref is self:\n            pass  # ok\n        else:\n            raise RuntimeError(\"WebGL doesn't yet support context sharing.\")\n\n        self._create_widget(size=size)\n\n    def _create_widget(self, size=None):\n        self._widget = VispyWidget(self._gen_event, size=size)\n        # Set glir parser on context and context.shared\n        context = self._vispy_canvas.context\n        context.shared.parser = WebGLGlirParser(self._widget)\n\n    def _reinit_widget(self):\n        self._vispy_canvas.set_current()\n\n        self._vispy_canvas.events.initialize()\n        self._vispy_canvas.events.resize(size=(self._widget.width,\n                                               self._widget.height))\n        self._vispy_canvas.events.draw()\n\n    def _vispy_warmup(self):\n        pass\n\n    # Uncommenting these makes the backend crash.\n    def _vispy_set_current(self):\n        pass\n\n    def _vispy_swap_buffers(self):\n        pass\n\n    def _vispy_set_title(self, title):\n        raise NotImplementedError()\n\n    def _vispy_get_fullscreen(self):\n        # We don't want error messages to show up when the user presses\n        # F11 to fullscreen the browser.\n        pass\n\n    def _vispy_set_fullscreen(self, fullscreen):\n        # We don't want error messages to show up when the user presses\n        # F11 to fullscreen the browser.\n        pass\n\n    def _vispy_get_size(self):\n        return (self._widget.width, self._widget.height)\n\n    def _vispy_set_size(self, w, h):\n        self._widget.width = w\n        self._widget.height = h\n\n    def _vispy_get_position(self):\n        raise NotImplementedError()\n\n    def _vispy_set_position(self, x, y):\n        logger.warning('IPython notebook canvas cannot be repositioned.')\n\n    def _vispy_set_visible(self, visible):\n        if not visible:\n            logger.warning('IPython notebook canvas cannot be hidden.')\n        else:\n            display(self._widget)\n            self._reinit_widget()\n\n    def _vispy_update(self):\n        ioloop = tornado.ioloop.IOLoop.current()\n        ioloop.add_callback(self._draw_event)\n\n    def _draw_event(self):\n        self._vispy_canvas.set_current()\n        self._vispy_canvas.events.draw()\n\n    def _vispy_close(self):\n        raise NotImplementedError()\n\n    def _vispy_mouse_release(self, **kwds):\n        # HACK: override this method from the base canvas in order to\n        # avoid breaking other backends.\n        kwds.update(self._vispy_mouse_data)\n        ev = self._vispy_canvas.events.mouse_release(**kwds)\n        if ev is None:\n            return\n        self._vispy_mouse_data['press_event'] = None\n        # TODO: this is a bit ugly, need to improve mouse button handling in\n        # app\n        ev._button = None\n        self._vispy_mouse_data['buttons'] = []\n        self._vispy_mouse_data['last_event'] = ev\n        return ev\n\n    # Generate vispy events according to upcoming JS events\n    _modifiers_map = {\n        'ctrl': keys.CONTROL,\n        'shift': keys.SHIFT,\n        'alt': keys.ALT,\n    }\n\n    def _gen_event(self, ev):\n        if self._vispy_canvas is None:\n            return\n        event_type = ev['type']\n        key_code = ev.get('key_code', None)\n        if key_code is None:\n            key, key_text = None, None\n        else:\n            if hasattr(keys, key_code):\n                key = getattr(keys, key_code)\n            else:\n                key = keys.Key(key_code)\n            # Generate the key text to pass to the event handler.\n            if key_code == 'SPACE':\n                key_text = ' '\n            else:\n                key_text = six.text_type(key_code)\n        # Process modifiers.\n        modifiers = ev.get('modifiers', None)\n        if modifiers:\n            modifiers = tuple([self._modifiers_map[modifier]\n                               for modifier in modifiers\n                               if modifier in self._modifiers_map])\n        if event_type == \"mouse_move\":\n            self._vispy_mouse_move(native=ev,\n                                   button=ev[\"button\"],\n                                   pos=ev[\"pos\"],\n                                   modifiers=modifiers,\n                                   )\n        elif event_type == \"mouse_press\":\n            self._vispy_mouse_press(native=ev,\n                                    pos=ev[\"pos\"],\n                                    button=ev[\"button\"],\n                                    modifiers=modifiers,\n                                    )\n        elif event_type == \"mouse_release\":\n            self._vispy_mouse_release(native=ev,\n                                      pos=ev[\"pos\"],\n                                      button=ev[\"button\"],\n                                      modifiers=modifiers,\n                                      )\n        elif event_type == \"mouse_wheel\":\n            self._vispy_canvas.events.mouse_wheel(native=ev,\n                                                  delta=ev[\"delta\"],\n                                                  pos=ev[\"pos\"],\n                                                  button=ev[\"button\"],\n                                                  modifiers=modifiers,\n                                                  )\n        elif event_type == \"key_press\":\n            self._vispy_canvas.events.key_press(native=ev,\n                                                key=key,\n                                                text=key_text,\n                                                modifiers=modifiers,\n                                                )\n        elif event_type == \"key_release\":\n            self._vispy_canvas.events.key_release(native=ev,\n                                                  key=key,\n                                                  text=key_text,\n                                                  modifiers=modifiers,\n                                                  )\n        elif event_type == \"resize\":\n            self._vispy_canvas.events.resize(native=ev,\n                                             size=ev[\"size\"])\n        elif event_type == \"paint\":\n            self._vispy_canvas.events.draw()\n\n\n# ------------------------------------------------------------------- Timer ---\nclass TimerBackend(BaseTimerBackend):\n    def __init__(self, *args, **kwargs):\n        super(TimerBackend, self).__init__(*args, **kwargs)\n        self._timer = tornado.ioloop.PeriodicCallback(\n            self._vispy_timer._timeout,\n            1000)\n\n    def _vispy_start(self, interval):\n        self._timer.callback_time = interval * 1000\n        self._timer.start()\n\n    def _vispy_stop(self):\n        self._timer.stop()\n\n\n# ---------------------------------------------------------- IPython Widget ---\nclass VispyWidget(DOMWidget):\n    _view_name = Unicode(\"VispyView\", sync=True)\n\n    width = Int(sync=True)\n    height = Int(sync=True)\n\n    def __init__(self, gen_event, **kwargs):\n        super(VispyWidget, self).__init__(**kwargs)\n        w, h = kwargs.get('size', (500, 200))\n        self.width = w\n        self.height = h\n        self.gen_event = gen_event\n        self.on_msg(self.events_received)\n\n    def events_received(self, _, msg):\n        if msg['msg_type'] == 'events':\n            events = msg['contents']\n            for ev in events:\n                self.gen_event(ev)\n\n    def send_glir_commands(self, commands):\n        # TODO: check whether binary websocket is available (ipython >= 3)\n        # Until IPython 3.0 is released, use base64.\n        array_serialization = 'base64'\n        # array_serialization = 'binary'\n        if array_serialization == 'base64':\n            msg = create_glir_message(commands, 'base64')\n            msg['array_serialization'] = 'base64'\n            self.send(msg)\n        elif array_serialization == 'binary':\n            msg = create_glir_message(commands, 'binary')\n            msg['array_serialization'] = 'binary'\n            # Remove the buffers from the JSON message: they will be sent\n            # independently via binary WebSocket.\n            buffers = msg.pop('buffers')\n            self.comm.send({\"method\": \"custom\", \"content\": msg},\n                           buffers=buffers)\n", "path": "vispy/app/backends/_ipynb_webgl.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n# Copyright (c) 2014, Vispy Development Team.\n# Distributed under the (new) BSD License. See LICENSE.txt for more info.\n\n\"\"\"\nVispy backend for the IPython notebook (WebGL approach).\n\"\"\"\n\nfrom __future__ import division\n\nfrom ..base import (BaseApplicationBackend, BaseCanvasBackend,\n                    BaseTimerBackend)\nfrom ._ipynb_util import create_glir_message\nfrom ...util import logger, keys\nfrom ...ext import six\nfrom vispy.gloo.glir import BaseGlirParser\nfrom vispy.app import Timer\n\n# Import for displaying Javascript on notebook\nimport os.path as op\n\n# -------------------------------------------------------------------- init ---\n\ncapability = dict(  # things that can be set by the backend\n    title=True,  # But it only applies to the dummy window :P\n    size=True,  # We cannot possibly say we dont, because Canvas always sets it\n    position=True,  # Dito\n    show=True,\n    vsync=False,\n    resizable=True,\n    decorate=False,\n    fullscreen=True,\n    context=True,\n    multi_window=False,\n    scroll=True,\n    parent=False,\n)\n\n\n# Init dummy objects needed to import this module withour errors.\n# These are all overwritten with imports from IPython (on success)\nDOMWidget = object\nUnicode = Int = Float = Bool = lambda *args, **kwargs: None\n\n# Try importing IPython\ntry:\n    import tornado\n    import IPython\n    IPYTHON_MAJOR_VERSION = IPython.version_info[0]\n    if IPYTHON_MAJOR_VERSION < 2:\n        raise RuntimeError('ipynb_webgl backend requires IPython >= 2.0')\n    from IPython.html.widgets import DOMWidget\n    from IPython.utils.traitlets import Unicode, Int\n    from IPython.display import display, Javascript\n    from IPython.html.nbextensions import install_nbextension\nexcept Exception as exp:\n    # raise ImportError(\"The WebGL backend requires IPython >= 2.0\")\n    available, testable, why_not, which = False, False, str(exp), None\nelse:\n    available, testable, why_not, which = True, False, None, None\n\n\n# ------------------------------------------------------------- application ---\ndef _prepare_js(force=False):\n    pkgdir = op.dirname(__file__)\n    jsdir = op.join(pkgdir, '../../html/static/js/')\n    # Make sure the JS files are installed to user directory (new argument\n    # in IPython 3.0).\n    if IPYTHON_MAJOR_VERSION >= 3:\n        kwargs = {'user': True}\n    else:\n        kwargs = {}\n    install_nbextension(op.join(jsdir, 'vispy.min.js'), overwrite=force,\n                        **kwargs)\n    backend_path = op.join(jsdir, 'webgl-backend.js')\n    with open(backend_path, 'r') as f:\n        script = f.read()\n    display(Javascript(script))\n\n\nclass ApplicationBackend(BaseApplicationBackend):\n\n    def __init__(self):\n        BaseApplicationBackend.__init__(self)\n        _prepare_js()\n\n    def _vispy_reuse(self):\n        _prepare_js()\n\n    def _vispy_get_backend_name(self):\n        return 'ipynb_webgl'\n\n    def _vispy_process_events(self):\n        # TODO: may be implemented later.\n        raise NotImplementedError()\n\n    def _vispy_run(self):\n        pass\n\n    def _vispy_quit(self):\n        pass\n\n    def _vispy_get_native_app(self):\n        return self\n\n\n# ------------------------------------------------------------------ canvas ---\nclass WebGLGlirParser(BaseGlirParser):\n    def __init__(self, widget):\n        self._widget = widget\n\n    def is_remote(self):\n        return True\n\n    def convert_shaders(self):\n        return 'es2'\n\n    def parse(self, commands):\n        self._widget.send_glir_commands(commands)\n\n\nclass CanvasBackend(BaseCanvasBackend):\n    # args are for BaseCanvasBackend, kwargs are for us.\n    def __init__(self, *args, **kwargs):\n        BaseCanvasBackend.__init__(self, *args)\n        # Maybe to ensure that exactly all arguments are passed?\n        title, size, position, show, vsync, resize, dec, fs, parent, context, \\\n            = self._process_backend_kwargs(kwargs)\n        self._context = context\n\n        # TODO: do something with context.config\n        # Take the context.\n        context.shared.add_ref('webgl', self)\n        if context.shared.ref is self:\n            pass  # ok\n        else:\n            raise RuntimeError(\"WebGL doesn't yet support context sharing.\")\n\n        self._create_widget(size=size)\n\n    def _create_widget(self, size=None):\n        self._widget = VispyWidget(self, size=size)\n        # Set glir parser on context and context.shared\n        context = self._vispy_canvas.context\n        context.shared.parser = WebGLGlirParser(self._widget)\n\n    def _reinit_widget(self):\n        self._vispy_canvas.set_current()\n\n        self._vispy_canvas.events.initialize()\n        self._vispy_canvas.events.resize(size=(self._widget.width,\n                                               self._widget.height))\n        self._vispy_canvas.events.draw()\n\n    def _vispy_warmup(self):\n        pass\n\n    # Uncommenting these makes the backend crash.\n    def _vispy_set_current(self):\n        pass\n\n    def _vispy_swap_buffers(self):\n        pass\n\n    def _vispy_set_title(self, title):\n        raise NotImplementedError()\n\n    def _vispy_get_fullscreen(self):\n        # We don't want error messages to show up when the user presses\n        # F11 to fullscreen the browser.\n        pass\n\n    def _vispy_set_fullscreen(self, fullscreen):\n        # We don't want error messages to show up when the user presses\n        # F11 to fullscreen the browser.\n        pass\n\n    def _vispy_get_size(self):\n        return (self._widget.width, self._widget.height)\n\n    def _vispy_set_size(self, w, h):\n        self._widget.width = w\n        self._widget.height = h\n\n    def _vispy_get_position(self):\n        raise NotImplementedError()\n\n    def _vispy_set_position(self, x, y):\n        logger.warning('IPython notebook canvas cannot be repositioned.')\n\n    def _vispy_set_visible(self, visible):\n        if not visible:\n            logger.warning('IPython notebook canvas cannot be hidden.')\n        else:\n            display(self._widget)\n            self._reinit_widget()\n\n    def _vispy_update(self):\n        ioloop = tornado.ioloop.IOLoop.current()\n        ioloop.add_callback(self._draw_event)\n\n    def _draw_event(self):\n        self._vispy_canvas.set_current()\n        self._vispy_canvas.events.draw()\n\n    def _vispy_close(self):\n        raise NotImplementedError()\n\n    def _vispy_mouse_release(self, **kwds):\n        # HACK: override this method from the base canvas in order to\n        # avoid breaking other backends.\n        kwds.update(self._vispy_mouse_data)\n        ev = self._vispy_canvas.events.mouse_release(**kwds)\n        if ev is None:\n            return\n        self._vispy_mouse_data['press_event'] = None\n        # TODO: this is a bit ugly, need to improve mouse button handling in\n        # app\n        ev._button = None\n        self._vispy_mouse_data['buttons'] = []\n        self._vispy_mouse_data['last_event'] = ev\n        return ev\n\n    # Generate vispy events according to upcoming JS events\n    _modifiers_map = {\n        'ctrl': keys.CONTROL,\n        'shift': keys.SHIFT,\n        'alt': keys.ALT,\n    }\n\n    def _gen_event(self, ev):\n        if self._vispy_canvas is None:\n            return\n        event_type = ev['type']\n        key_code = ev.get('key_code', None)\n        if key_code is None:\n            key, key_text = None, None\n        else:\n            if hasattr(keys, key_code):\n                key = getattr(keys, key_code)\n            else:\n                key = keys.Key(key_code)\n            # Generate the key text to pass to the event handler.\n            if key_code == 'SPACE':\n                key_text = ' '\n            else:\n                key_text = six.text_type(key_code)\n        # Process modifiers.\n        modifiers = ev.get('modifiers', None)\n        if modifiers:\n            modifiers = tuple([self._modifiers_map[modifier]\n                               for modifier in modifiers\n                               if modifier in self._modifiers_map])\n        if event_type == \"mouse_move\":\n            self._vispy_mouse_move(native=ev,\n                                   button=ev[\"button\"],\n                                   pos=ev[\"pos\"],\n                                   modifiers=modifiers,\n                                   )\n        elif event_type == \"mouse_press\":\n            self._vispy_mouse_press(native=ev,\n                                    pos=ev[\"pos\"],\n                                    button=ev[\"button\"],\n                                    modifiers=modifiers,\n                                    )\n        elif event_type == \"mouse_release\":\n            self._vispy_mouse_release(native=ev,\n                                      pos=ev[\"pos\"],\n                                      button=ev[\"button\"],\n                                      modifiers=modifiers,\n                                      )\n        elif event_type == \"mouse_wheel\":\n            self._vispy_canvas.events.mouse_wheel(native=ev,\n                                                  delta=ev[\"delta\"],\n                                                  pos=ev[\"pos\"],\n                                                  button=ev[\"button\"],\n                                                  modifiers=modifiers,\n                                                  )\n        elif event_type == \"key_press\":\n            self._vispy_canvas.events.key_press(native=ev,\n                                                key=key,\n                                                text=key_text,\n                                                modifiers=modifiers,\n                                                )\n        elif event_type == \"key_release\":\n            self._vispy_canvas.events.key_release(native=ev,\n                                                  key=key,\n                                                  text=key_text,\n                                                  modifiers=modifiers,\n                                                  )\n        elif event_type == \"resize\":\n            self._vispy_canvas.events.resize(native=ev,\n                                             size=ev[\"size\"])\n        elif event_type == \"paint\":\n            self._vispy_canvas.events.draw()\n\n\n# ------------------------------------------------------------------- Timer ---\nclass TimerBackend(BaseTimerBackend):\n    def __init__(self, *args, **kwargs):\n        super(TimerBackend, self).__init__(*args, **kwargs)\n        self._timer = tornado.ioloop.PeriodicCallback(\n            self._vispy_timer._timeout,\n            1000)\n\n    def _vispy_start(self, interval):\n        self._timer.callback_time = interval * 1000\n        self._timer.start()\n\n    def _vispy_stop(self):\n        self._timer.stop()\n\n\n# ---------------------------------------------------------- IPython Widget ---\ndef _stop_timers(canvas):\n    \"\"\"Stop all timers in a canvas.\"\"\"\n    for attr in dir(canvas):\n        try:\n            attr_obj = getattr(canvas, attr)\n        except NotImplementedError:\n            # This try/except is needed because canvas.position raises\n            # an error (it is not implemented in this backend).\n            attr_obj = None\n        if isinstance(attr_obj, Timer):\n            attr_obj.stop()\n\n\nclass VispyWidget(DOMWidget):\n    _view_name = Unicode(\"VispyView\", sync=True)\n\n    width = Int(sync=True)\n    height = Int(sync=True)\n\n    def __init__(self, canvas_backend, **kwargs):\n        super(VispyWidget, self).__init__(**kwargs)\n        w, h = kwargs.get('size', (500, 200))\n        self.width = w\n        self.height = h\n        self.canvas_backend = canvas_backend\n        self.gen_event = canvas_backend._gen_event\n        self.on_msg(self.events_received)\n\n    def events_received(self, _, msg):\n        if msg['msg_type'] == 'events':\n            events = msg['contents']\n            for ev in events:\n                self.gen_event(ev)\n        elif msg['msg_type'] == 'status':\n            if msg['contents'] == 'removed':\n                # Stop all timers associated to the widget.\n                _stop_timers(self.canvas_backend._vispy_canvas)\n\n    def send_glir_commands(self, commands):\n        # TODO: check whether binary websocket is available (ipython >= 3)\n        # Until IPython 3.0 is released, use base64.\n        array_serialization = 'base64'\n        # array_serialization = 'binary'\n        if array_serialization == 'base64':\n            msg = create_glir_message(commands, 'base64')\n            msg['array_serialization'] = 'base64'\n            self.send(msg)\n        elif array_serialization == 'binary':\n            msg = create_glir_message(commands, 'binary')\n            msg['array_serialization'] = 'binary'\n            # Remove the buffers from the JSON message: they will be sent\n            # independently via binary WebSocket.\n            buffers = msg.pop('buffers')\n            self.comm.send({\"method\": \"custom\", \"content\": msg},\n                           buffers=buffers)\n", "path": "vispy/app/backends/_ipynb_webgl.py"}]}