RGBMatrixEmulator 0.11.4__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. RGBMatrixEmulator/__init__.py +5 -0
  2. RGBMatrixEmulator/adapters/__init__.py +68 -0
  3. RGBMatrixEmulator/adapters/base.py +111 -0
  4. RGBMatrixEmulator/adapters/browser_adapter/README.md +89 -0
  5. RGBMatrixEmulator/adapters/browser_adapter/__init__.py +0 -0
  6. RGBMatrixEmulator/adapters/browser_adapter/adapter.py +53 -0
  7. RGBMatrixEmulator/adapters/browser_adapter/request_handlers/__init__.py +3 -0
  8. RGBMatrixEmulator/adapters/browser_adapter/request_handlers/image.py +10 -0
  9. RGBMatrixEmulator/adapters/browser_adapter/request_handlers/image_web_socket.py +30 -0
  10. RGBMatrixEmulator/adapters/browser_adapter/request_handlers/main.py +9 -0
  11. RGBMatrixEmulator/adapters/browser_adapter/server.py +73 -0
  12. RGBMatrixEmulator/adapters/browser_adapter/static/assets/client.js +91 -0
  13. RGBMatrixEmulator/adapters/browser_adapter/static/assets/icon.ico +0 -0
  14. RGBMatrixEmulator/adapters/browser_adapter/static/assets/styles.css +25 -0
  15. RGBMatrixEmulator/adapters/browser_adapter/static/index.html +144 -0
  16. RGBMatrixEmulator/adapters/pygame_adapter.py +71 -0
  17. RGBMatrixEmulator/adapters/sixel_adapter.py +72 -0
  18. RGBMatrixEmulator/adapters/terminal_adapter.py +36 -0
  19. RGBMatrixEmulator/adapters/tkinter_adapter.py +83 -0
  20. RGBMatrixEmulator/adapters/turtle_adapter.py +95 -0
  21. RGBMatrixEmulator/emulators/__init__.py +0 -0
  22. RGBMatrixEmulator/emulators/canvas.py +39 -0
  23. RGBMatrixEmulator/emulators/matrix.py +50 -0
  24. RGBMatrixEmulator/emulators/options.py +188 -0
  25. RGBMatrixEmulator/graphics/__init__.py +155 -0
  26. RGBMatrixEmulator/graphics/color.py +36 -0
  27. RGBMatrixEmulator/graphics/font.py +34 -0
  28. RGBMatrixEmulator/icon.ico +0 -0
  29. RGBMatrixEmulator/icon.png +0 -0
  30. RGBMatrixEmulator/logger.py +29 -0
  31. RGBMatrixEmulator/version.py +5 -0
  32. rgbmatrixemulator-0.11.4.data/data/RGBMatrixEmulator/client.js +91 -0
  33. rgbmatrixemulator-0.11.4.data/data/RGBMatrixEmulator/icon.ico +0 -0
  34. rgbmatrixemulator-0.11.4.data/data/RGBMatrixEmulator/icon.png +0 -0
  35. rgbmatrixemulator-0.11.4.data/data/RGBMatrixEmulator/index.html +144 -0
  36. rgbmatrixemulator-0.11.4.data/data/RGBMatrixEmulator/styles.css +25 -0
  37. rgbmatrixemulator-0.11.4.data/data/docs/LICENSE +9 -0
  38. rgbmatrixemulator-0.11.4.data/data/docs/README.md +160 -0
  39. rgbmatrixemulator-0.11.4.dist-info/METADATA +186 -0
  40. rgbmatrixemulator-0.11.4.dist-info/RECORD +43 -0
  41. rgbmatrixemulator-0.11.4.dist-info/WHEEL +5 -0
  42. rgbmatrixemulator-0.11.4.dist-info/licenses/LICENSE +9 -0
@@ -0,0 +1,188 @@
1
+ import json, os, pprint, sys
2
+
3
+ from RGBMatrixEmulator.adapters import ADAPTER_TYPES
4
+ from RGBMatrixEmulator.logger import Logger
5
+
6
+
7
+ class RGBMatrixOptions:
8
+ def __init__(self):
9
+ self.hardware_mapping = 'EMULATED'
10
+ self.rows = 32
11
+ self.cols = 32
12
+ self.chain_length = 1
13
+ self.parallel = 1
14
+ self.row_address_type = 0
15
+ self.multiplexing = 0
16
+ self.pwm_bits = 0
17
+ self.brightness = 100
18
+ self.pwm_lsb_nanoseconds = 130
19
+ self.led_rgb_sequence = 'RGB-EMULATED'
20
+ self.show_refresh_rate = 0
21
+ self.gpio_slowdown = None
22
+ self.disable_hardware_pulsing = False
23
+
24
+ emulator_config = RGBMatrixEmulatorConfig()
25
+
26
+ if emulator_config.display_adapter.lower() in ADAPTER_TYPES:
27
+ self.display_adapter = ADAPTER_TYPES[emulator_config.display_adapter.lower()]
28
+ elif len(ADAPTER_TYPES.keys()) > 0:
29
+ adapter_types = ', '.join('"{}"'.format(key) for key in ADAPTER_TYPES.keys())
30
+
31
+ # Try to set it to the emulator default, but if it failed to load, pick the first one that did.
32
+ if emulator_config.DEFAULT_CONFIG.get('display_adapter') in ADAPTER_TYPES:
33
+ default_adapter = emulator_config.DEFAULT_CONFIG.get('display_adapter')
34
+ else:
35
+ default_adapter = list(ADAPTER_TYPES.keys())[0]
36
+
37
+ Logger.warning('"{}" display adapter option not recognized. Valid adapters are {}. Defaulting to "{}"...'.format(
38
+ emulator_config.display_adapter,
39
+ adapter_types,
40
+ default_adapter
41
+ )
42
+ )
43
+ self.display_adapter = ADAPTER_TYPES[default_adapter]
44
+ else:
45
+ Logger.critical("Failed to find a valid display adapter to load! Check that you have installed dependencies required for your configured adapter.")
46
+
47
+ sys.exit(1)
48
+
49
+ self.pixel_style = emulator_config.DEFAULT_CONFIG.get('pixel_style')
50
+ config_pixel_style = emulator_config.pixel_style.lower()
51
+
52
+ if config_pixel_style in emulator_config.VALID_PIXEL_STYLES:
53
+ if config_pixel_style != self.pixel_style:
54
+ if self.display_adapter.SUPPORTS_ALTERNATE_PIXEL_STYLE:
55
+ self.pixel_style = emulator_config.pixel_style
56
+ else:
57
+ Logger.warning('"{}" pixel style option is not supported by adapter "{}". Defaulting to "square"...'.format(config_pixel_style, emulator_config.display_adapter.lower()))
58
+ else:
59
+ Logger.warning('"{}" pixel style option not recognized. Valid options are "square", "circle". Defaulting to "square"...'.format(config_pixel_style))
60
+
61
+ self.pixel_size = emulator_config.pixel_size
62
+ self.pixel_outline = emulator_config.DEFAULT_CONFIG['pixel_outline']
63
+ self.pixel_outline = emulator_config.pixel_outline
64
+ self.browser = emulator_config.browser
65
+
66
+ if emulator_config.suppress_font_warnings:
67
+ import bdfparser
68
+
69
+ bdfparser.warnings.simplefilter("ignore")
70
+
71
+ def window_size(self):
72
+ return (self.cols * self.pixel_size * self.chain_length, self.rows * self.pixel_size * self.parallel)
73
+
74
+ def window_size_str(self, pixel_text=""):
75
+ width, height = self.window_size()
76
+
77
+ return f"{width} x {height} {pixel_text}"
78
+
79
+ class RGBMatrixEmulatorConfig:
80
+
81
+ __CONFIG_PATH = 'emulator_config.json'
82
+
83
+ VALID_PIXEL_STYLES = ['square', 'circle']
84
+ DEFAULT_CONFIG = {
85
+ 'pixel_outline': 0,
86
+ 'pixel_size': 16,
87
+ 'pixel_style': 'square',
88
+ 'display_adapter': 'browser',
89
+ 'suppress_font_warnings': False,
90
+ 'suppress_adapter_load_errors': False,
91
+ 'browser': {
92
+ '_comment': 'For use with the browser adapter only.',
93
+ 'port': 8888,
94
+ 'target_fps': 24,
95
+ 'fps_display': False,
96
+ 'quality': 70,
97
+ 'image_border': True,
98
+ 'debug_text': False,
99
+ 'image_format': 'JPEG'
100
+ },
101
+ "log_level": "info"
102
+ }
103
+
104
+ def __init__(self):
105
+ self.config = self.__load_config()
106
+ self.default_config = self.DEFAULT_CONFIG
107
+
108
+ RGBMatrixEmulatorConfig.Utils.set_attributes(self)
109
+
110
+ def __load_config(self):
111
+ if os.path.exists(self.__CONFIG_PATH):
112
+ with open(self.__CONFIG_PATH) as f:
113
+ config = json.load(f)
114
+
115
+ return config
116
+
117
+ with open(self.__CONFIG_PATH, 'w') as f:
118
+ json.dump(self.DEFAULT_CONFIG, f, indent=4)
119
+
120
+ return self.DEFAULT_CONFIG
121
+
122
+ def __str__(self):
123
+ return RGBMatrixEmulatorConfig.Utils.to_str(self)
124
+
125
+
126
+ class ChildConfig:
127
+ def __init__(self, config, default_config):
128
+ self.config = config
129
+ self.default_config = default_config
130
+
131
+ RGBMatrixEmulatorConfig.Utils.set_attributes(self)
132
+
133
+ def __str__(self):
134
+ return RGBMatrixEmulatorConfig.Utils.to_str(self)
135
+
136
+
137
+ class Utils:
138
+ def to_str(obj):
139
+ '''
140
+ Pretty prints the config object from dict.
141
+ '''
142
+ printer = pprint.PrettyPrinter(sort_dicts=False)
143
+ return "\n".join([obj.__repr__(), printer.pformat(RGBMatrixEmulatorConfig.Utils.to_dict(obj))])
144
+
145
+ def to_dict(obj):
146
+ '''
147
+ Recursively recreates the config dict from child config objects.
148
+ '''
149
+ config = {}
150
+ for key in obj.__dict__.keys():
151
+ if key in ['config', 'default_config'] or key[0] == '_':
152
+ continue
153
+
154
+ value = obj.__dict__.get(key)
155
+
156
+ if isinstance(value, RGBMatrixEmulatorConfig.ChildConfig):
157
+ value = RGBMatrixEmulatorConfig.Utils.to_dict(value)
158
+
159
+ config[key] = value
160
+
161
+ return config
162
+
163
+ def set_attributes(obj):
164
+ '''
165
+ Dynamically set attributes loaded into config and default config variables.
166
+
167
+ Numbers, strings, and arrays are stored natively. Nested dicts are parsed into RGBMatrixEmulatorChildConfig objects recursively.
168
+ '''
169
+ for key in obj.default_config.keys():
170
+ if key in obj.config:
171
+ value = obj.config.get(key)
172
+ default = obj.default_config.get(key)
173
+ else:
174
+ value = obj.default_config.get(key)
175
+ default = value
176
+
177
+ Logger.warning("Emulator config is missing key '{}', falling back to default '{}'. Consider adding this to your emulator config file.".format(key, value))
178
+
179
+ RGBMatrixEmulatorConfig.Utils.set_attribute(obj, key, value, default)
180
+
181
+ def set_attribute(obj, key, value, default):
182
+ '''
183
+ Store the value as an attribute or delegate to the RGBMatrixEmulatorChildConfig to parse into a new node.
184
+ '''
185
+ if isinstance(value, dict):
186
+ obj.__setattr__(key, RGBMatrixEmulatorConfig.ChildConfig(value, default))
187
+ else:
188
+ obj.__setattr__(key, value)
@@ -0,0 +1,155 @@
1
+ from RGBMatrixEmulator.graphics.color import Color
2
+ from RGBMatrixEmulator.graphics.font import Font
3
+
4
+
5
+ def DrawText(canvas, font, x, y, color, text):
6
+ # Early return for empty string prevents bugs in bdfparser library
7
+ # and makes good sense anyway
8
+ if len(text) == 0:
9
+ return
10
+
11
+ # Support multiple spacings based on device width
12
+ character_widths = [__actual_width(font, letter) for letter in text]
13
+ first_char_width = character_widths[0]
14
+ max_char_width = max(character_widths)
15
+ total_width = sum(character_widths)
16
+
17
+ # Offscreen to the left, adjust by first character width
18
+ if x < 0:
19
+ adjustment = abs(x + first_char_width) // first_char_width
20
+ text = text[adjustment:]
21
+ if adjustment:
22
+ x += first_char_width * adjustment
23
+
24
+ # Offscreen to the right, rough adjustment by max width
25
+ if (total_width + x) > canvas.width:
26
+ text = text[: ((canvas.width + 1) // max_char_width) + 2]
27
+
28
+ # Draw the text!
29
+ if len(text) != 0:
30
+ # Ensure text doesn't get drawn as multiple lines
31
+ linelimit = len(text) * (font.headers['fbbx'] + 1)
32
+
33
+ text_map = font.bdf_font.draw(text, linelimit, missing=font.default_character).todata(2)
34
+ font_y_offset = -(font.headers['fbby'] + font.headers['fbbyoff'])
35
+
36
+ for y2, row in enumerate(text_map):
37
+ for x2, value in enumerate(row):
38
+ if value == 1:
39
+ if isinstance(color, tuple):
40
+ canvas.SetPixel(x + x2, y + y2 + font_y_offset, *color)
41
+ else:
42
+ canvas.SetPixel(x + x2, y + y2 + font_y_offset, color.red, color.green, color.blue)
43
+
44
+ return total_width
45
+
46
+ def DrawLine(canvas, x1, y1, x2, y2, color):
47
+ int_points = __coerce_int(x1, y1, x2, y2)
48
+ rows, cols = __line(*int_points)
49
+
50
+ for point in zip(rows, cols):
51
+ if isinstance(color, tuple):
52
+ canvas.SetPixel(*point, *color)
53
+ else:
54
+ canvas.SetPixel(*point, color.red, color.green, color.blue)
55
+
56
+ def DrawCircle(canvas, x, y, r, color):
57
+ int_points = __coerce_int(x, y)
58
+ rows, cols = __circle_perimeter(*int_points, r)
59
+
60
+ for point in zip(rows, cols):
61
+ if isinstance(color, tuple):
62
+ canvas.SetPixel(*point, *color)
63
+ else:
64
+ canvas.SetPixel(*point, color.red, color.green, color.blue)
65
+
66
+ def __actual_width(font, letter):
67
+ '''
68
+ Returns the actual width of the letter in the font. If the font doesn't contain a glyph for this letter, it falls back to
69
+ the width of the default character (?) to prevent division by 0.
70
+ '''
71
+ width = font.CharacterWidth(ord(letter))
72
+
73
+ if width > 0:
74
+ return width
75
+
76
+ return font.CharacterWidth(font.default_character.cp())
77
+
78
+ def __coerce_int(*values):
79
+ return [int(value) for value in values]
80
+
81
+ def __line(x1, y1, x2, y2):
82
+ '''
83
+ Line drawing algorithm
84
+
85
+ Extracted from scikit-image:
86
+ https://github.com/scikit-image/scikit-image/blob/00177e14097237ef20ed3141ed454bc81b308f82/skimage/draw/_draw.pyx#L44
87
+ '''
88
+ steep = 0
89
+ r = x1
90
+ c = y1
91
+ dr = abs(x2 - x1)
92
+ dc = abs(y2 - y1)
93
+
94
+ rr = [0] * (max(dc, dr) + 1)
95
+ cc = [0] * (max(dc, dr) + 1)
96
+
97
+ if (y2 - c) > 0:
98
+ sc = 1
99
+ else:
100
+ sc = -1
101
+ if (x2 - r) > 0:
102
+ sr = 1
103
+ else:
104
+ sr = -1
105
+ if dr > dc:
106
+ steep = 1
107
+ c, r = r, c
108
+ dc, dr = dr, dc
109
+ sc, sr = sr, sc
110
+ d = (2 * dr) - dc
111
+
112
+ for i in range(dc):
113
+ if steep:
114
+ rr[i] = c
115
+ cc[i] = r
116
+ else:
117
+ rr[i] = r
118
+ cc[i] = c
119
+ while d >= 0:
120
+ r = r + sr
121
+ d = d - (2 * dc)
122
+ c = c + sc
123
+ d = d + (2 * dr)
124
+
125
+ rr[dc] = x2
126
+ cc[dc] = y2
127
+
128
+ return (rr, cc)
129
+
130
+ def __circle_perimeter(x, y, radius):
131
+ '''
132
+ Bresenham circle algorithm
133
+
134
+ Extracted from scikit-image
135
+ https://github.com/scikit-image/scikit-image/blob/00177e14097237ef20ed3141ed454bc81b308f82/skimage/draw/_draw.pyx#L248
136
+ '''
137
+ rr = list()
138
+ cc = list()
139
+
140
+ c = 0
141
+ r = radius
142
+ d = 3 - 2 * radius
143
+
144
+ while r >= c:
145
+ rr.extend([_ + x for _ in [r, -r, r, -r, c, -c, c, -c]])
146
+ cc.extend([_ + y for _ in [c, c, -c, -c, r, r, -r, -r]])
147
+
148
+ if d < 0:
149
+ d += 4 * c + 6
150
+ else:
151
+ d += 4 * (c - r) + 10
152
+ r -= 1
153
+ c += 1
154
+
155
+ return (rr, cc)
@@ -0,0 +1,36 @@
1
+ class Color:
2
+ def __init__(self, r = 0, g = 0, b = 0):
3
+ self.red = r
4
+ self.green = g
5
+ self.blue = b
6
+
7
+ @classmethod
8
+ def adjust_brightness(cls, pixel, alpha, to_int = False):
9
+ p = tuple(channel * alpha for channel in pixel)
10
+
11
+ if to_int:
12
+ p = tuple(int(channel) for channel in pixel)
13
+
14
+ return p
15
+
16
+ @classmethod
17
+ def to_hex(cls, pixel):
18
+ return "#%02x%02x%02x" % pixel
19
+
20
+ @classmethod
21
+ def BLACK(cls):
22
+ return (0, 0, 0)
23
+
24
+ @classmethod
25
+ def RED(cls):
26
+ return (255, 0, 0)
27
+
28
+ @classmethod
29
+ def GREEN(cls):
30
+ return (0, 255, 0)
31
+
32
+ @classmethod
33
+ def BLUE(cls):
34
+ return (0, 0, 255)
35
+
36
+
@@ -0,0 +1,34 @@
1
+ import bdfparser
2
+
3
+
4
+ class Font:
5
+ def __init__(self):
6
+ self.bdf_font = None
7
+ self.headers = {}
8
+ self.spacing = {}
9
+
10
+ def LoadFont(self, path):
11
+ self.bdf_font = bdfparser.Font(path)
12
+ self.headers = self.bdf_font.headers
13
+ self.props = self.bdf_font.props
14
+
15
+ # All rpi-rgb-led-matrix fonts have a character at 0xFFFD to represent a missing character
16
+ # Cache this for use later so we don't have to constantly look it up
17
+ self.default_character = self.bdf_font.glyphbycp(0xFFFD)
18
+
19
+ def CharacterWidth(self, char):
20
+ # Missing glyphs return 0 width in rpi-rgb-led-matrix
21
+ if self.bdf_font == None or not self.bdf_font.glyphbycp(char):
22
+ return 0
23
+
24
+ return self.bdf_font.glyphbycp(char).meta['dwx0']
25
+
26
+ @property
27
+ def height(self):
28
+ if self.bdf_font is None: return -1
29
+ return self.headers['fbby']
30
+
31
+ @property
32
+ def baseline(self):
33
+ if self.bdf_font is None: return 0
34
+ return self.headers['fbby'] + self.headers['fbbyoff']
Binary file
Binary file
@@ -0,0 +1,29 @@
1
+ import json, logging
2
+
3
+ # Try to load the config from file. (Default: INFO)
4
+ try:
5
+ with open("emulator_config.json") as config_file:
6
+ log_level_name = json.load(config_file).get("log_level", 'INFO').upper()
7
+ log_level = getattr(logging, log_level_name)
8
+ except:
9
+ log_level = logging.INFO
10
+
11
+ # Create a Logger
12
+ Logger = logging.getLogger('RGBME')
13
+ Logger.setLevel(log_level)
14
+
15
+ # Create console handler and set the log level
16
+ ch = logging.StreamHandler()
17
+ ch.setLevel(log_level)
18
+
19
+ # Create formatter
20
+ formatter = logging.Formatter(
21
+ '[%(asctime)s] [%(name)s] [%(levelname)s]: %(message)s',
22
+ datefmt = '%Y-%m-%d %H:%M:%S'
23
+ )
24
+
25
+ # Add formatter to console handler
26
+ ch.setFormatter(formatter)
27
+
28
+ # Add console handler to Logger
29
+ Logger.addHandler(ch)
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env python
2
+
3
+ # package version
4
+ __version__ = '0.11.4'
5
+ """Installed version of RGBMatrixEmulator."""
@@ -0,0 +1,91 @@
1
+ function init() {
2
+ const WS_RETRY_DELAY = 2000;
3
+ const FPS_DEFAULT = 24;
4
+
5
+ let img = document.getElementById("liveImg");
6
+ let fpsText = document.getElementById("fps");
7
+ let fpsTarget = parseInt(document.getElementById("targetFps").value) || FPS_DEFAULT;
8
+
9
+ let requestStartTime = performance.now();
10
+ let startTime = performance.now();
11
+ let time = 0;
12
+ let requestTime = 0;
13
+ let timeSmoothing = 0.9; // larger=more smoothing
14
+ let requestTimeSmoothing = 0.2; // larger=more smoothing
15
+ let targetTime = 1000 / fpsTarget;
16
+
17
+ let socket = generateSocket();
18
+
19
+ function requestImage() {
20
+ requestStartTime = performance.now();
21
+ socket.send('more');
22
+ }
23
+
24
+ function generateSocket() {
25
+ let path = location.pathname;
26
+
27
+ if (path.endsWith("index.html")) {
28
+ path = path.substring(0, path.length - "index.html".length);
29
+ }
30
+
31
+ if(!path.endsWith("/")) {
32
+ path = path + "/";
33
+ }
34
+
35
+ let wsProtocol = (location.protocol === "https:") ? "wss://" : "ws://";
36
+ let ws = new WebSocket(wsProtocol + location.host + path + "websocket");
37
+
38
+ ws.binaryType = 'arraybuffer';
39
+
40
+ ws.onopen = function() {
41
+ console.log("RGBME WebSocket connection established!");
42
+ startTime = performance.now();
43
+ requestImage();
44
+ };
45
+
46
+ ws.onclose = function() {
47
+ // Handle retries by recreating the connection to websocket.
48
+ console.warn(`RGBME WebSocket connection lost. Retrying in ${WS_RETRY_DELAY / 1000}s.`)
49
+ setTimeout(function() {
50
+ // We generate socket with a timeout to make sure server has time to recover.
51
+ socket = generateSocket();
52
+ }, WS_RETRY_DELAY);
53
+ }
54
+
55
+ ws.onerror = function() {
56
+ ws.close();
57
+ }
58
+
59
+ ws.onmessage = function(evt) {
60
+ let arrayBuffer = evt.data;
61
+ let blob = new Blob([new Uint8Array(arrayBuffer)], {type: "image/jpeg"});
62
+ let old_img = img.src.slice()
63
+ img.src = window.URL.createObjectURL(blob);
64
+ window.URL.revokeObjectURL(old_img);
65
+
66
+ let endTime = performance.now();
67
+ let currentTime = endTime - startTime;
68
+ // smooth with moving average
69
+ time = (time * timeSmoothing) + (currentTime * (1.0 - timeSmoothing));
70
+ startTime = endTime;
71
+ let fps = Math.round(1000 / time);
72
+
73
+ if (fpsText) {
74
+ fpsText.textContent = fps;
75
+ }
76
+
77
+ let currentRequestTime = performance.now() - requestStartTime;
78
+ // smooth with moving average
79
+ requestTime = (requestTime * requestTimeSmoothing) + (currentRequestTime * (1.0 - requestTimeSmoothing));
80
+ let timeout = Math.max(0, targetTime - requestTime);
81
+
82
+ setTimeout(requestImage, timeout);
83
+ };
84
+
85
+ return ws;
86
+ }
87
+
88
+ console.log(`TARGET FPS: ${fpsTarget}`);
89
+ };
90
+
91
+ init();
@@ -0,0 +1,144 @@
1
+ <html>
2
+ <head>
3
+ <title>{{ adapter.emulator_details_text() }}</title>
4
+ <link rel="shortcut icon" href="assets/icon.ico">
5
+ <link rel="stylesheet" href="assets/styles.css">
6
+ </head>
7
+ <body>
8
+ <img id="liveImg" class={{ "" if adapter.options.browser.image_border else "no-border" }} />
9
+
10
+ {% if adapter.options.browser.fps_display %}
11
+ <div id="fpsDisplay">
12
+ Frames per second: <span id="fps">0</span>
13
+ </div>
14
+ {% end %}
15
+
16
+ {% if adapter.options.browser.debug_text %}
17
+ <div id="emulatorDetails">
18
+ <h2>{{ adapter.emulator_details_text() }}</h2>
19
+
20
+ <div class="emulatorDetailsTableRow">
21
+ <div id="matrixDetails" class="tableContainer">
22
+ <h3>Matrix Details:</h3>
23
+
24
+ <table>
25
+ <tbody>
26
+ <tr>
27
+ <td>
28
+ Matrix Width:
29
+ </td>
30
+ <td>
31
+ {{ adapter.options.cols }}
32
+ </td>
33
+ </tr>
34
+ <tr>
35
+ <td>
36
+ Matrix Height:
37
+ </td>
38
+ <td>
39
+ {{ adapter.options.rows }}
40
+ </td>
41
+ </td>
42
+ </tr>
43
+ <tr>
44
+ <td>
45
+ Image Size:
46
+ </td>
47
+ <td>
48
+ {{ adapter.options.window_size_str(pixel_text="px") }}
49
+ </td>
50
+ </tr>
51
+ <tr>
52
+ <td>
53
+ Chain Length:
54
+ </td>
55
+ <td>
56
+ {{ adapter.options.chain_length }}
57
+ </td>
58
+ </tr>
59
+ <tr>
60
+ <td>
61
+ Parallel Chains:
62
+ </td>
63
+ <td>
64
+ {{ adapter.options.parallel }}
65
+ </td>
66
+ </tr>
67
+ <tr>
68
+ <td>
69
+ Pixel Size:
70
+ </td>
71
+ <td>
72
+ {{ adapter.options.pixel_size }}
73
+ </td>
74
+ </tr>
75
+ <tr>
76
+ <td>
77
+ Pixel Style:
78
+ </td>
79
+ <td>
80
+ {{ adapter.options.pixel_style }}
81
+ </td>
82
+ </tr>
83
+ <tr>
84
+ <td>
85
+ Adapter:
86
+ </td>
87
+ <td>
88
+ {{ adapter.__class__.__name__ }}
89
+ </td>
90
+ </tr>
91
+ </tbody>
92
+ </table>
93
+ </div>
94
+
95
+ <div id="browserDetails" class="tableContainer">
96
+ <h3>Browser Details:</h3>
97
+
98
+ <table>
99
+ <tbody>
100
+ <tr>
101
+ <td>
102
+ Port:
103
+ </td>
104
+ <td>
105
+ {{ adapter.options.browser.port }}
106
+ </td>
107
+ </tr>
108
+ <tr>
109
+ <td>
110
+ Target FPS:
111
+ </td>
112
+ <td>
113
+ {{ adapter.options.browser.target_fps }}
114
+ </td>
115
+ </td>
116
+ </tr>
117
+ <tr>
118
+ <td>
119
+ FPS Display Enabled:
120
+ </td>
121
+ <td>
122
+ {{ adapter.options.browser.fps_display }}
123
+ </td>
124
+ </tr>
125
+ <tr>
126
+ <td>
127
+ Image Quality:
128
+ </td>
129
+ <td>
130
+ {{ adapter.options.browser.quality }}
131
+ </td>
132
+ </tr>
133
+ </tbody>
134
+ </table>
135
+ </div>
136
+ </div>
137
+ </div>
138
+ {% end %}
139
+
140
+ <input id="targetFps" type="hidden" value={{ adapter.options.browser.target_fps }} />
141
+
142
+ <script type="text/javascript" src="assets/client.js"></script>
143
+ </body>
144
+ </html>