1#! /usr/bin/python 2 3# Copyright 2019 The ANGLE Project Authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6# 7# gen_overlay_widgets.py: 8# Code generation for overlay widgets. Should be run when the widgets declaration file, 9# overlay_widgets.json, is changed. 10# NOTE: don't run this script directly. Run scripts/run_code_generation.py. 11 12from datetime import date 13import json 14import sys 15 16out_file = 'Overlay_autogen.cpp' 17in_file = 'overlay_widgets.json' 18 19template_out_file = u"""// GENERATED FILE - DO NOT EDIT. 20// Generated by {script_name} using data from {input_file_name}. 21// 22// Copyright {copyright_year} The ANGLE Project Authors. All rights reserved. 23// Use of this source code is governed by a BSD-style license that can be 24// found in the LICENSE file. 25// 26// {out_file_name}: 27// Autogenerated overlay widget declarations. 28 29#include "libANGLE/renderer/driver_utils.h" 30#include "libANGLE/Overlay.h" 31#include "libANGLE/OverlayWidgets.h" 32#include "libANGLE/Overlay_font_autogen.h" 33 34namespace gl 35{{ 36using namespace overlay; 37 38namespace 39{{ 40int GetFontSize(int fontSize, bool largeFont) 41{{ 42 if (largeFont && fontSize > 0) 43 {{ 44 return fontSize - 1; 45 }} 46 return fontSize; 47}} 48}} // anonymous namespace 49 50void Overlay::initOverlayWidgets() 51{{ 52 const bool kLargeFont = rx::IsAndroid(); 53 54 {init_widgets} 55}} 56 57}} // namespace gl 58 59""" 60 61template_init_widget = u"""{{ 62const int32_t fontSize = GetFontSize({font_size}, kLargeFont); 63const int32_t offsetX = {offset_x}; 64const int32_t offsetY = {offset_y}; 65const int32_t width = {width}; 66const int32_t height = {height}; 67 68widget->{subwidget}type = WidgetType::{type}; 69widget->{subwidget}fontSize = fontSize; 70widget->{subwidget}coords[0] = {coord0}; 71widget->{subwidget}coords[1] = {coord1}; 72widget->{subwidget}coords[2] = {coord2}; 73widget->{subwidget}coords[3] = {coord3}; 74widget->{subwidget}color[0] = {color_r}; 75widget->{subwidget}color[1] = {color_g}; 76widget->{subwidget}color[2] = {color_b}; 77widget->{subwidget}color[3] = {color_a}; 78}} 79""" 80 81 82def extract_type_and_constructor(properties): 83 constructor = properties['type'] 84 args_separated = constructor.split('(', 1) 85 if len(args_separated) == 1: 86 return constructor, constructor 87 88 type_no_constructor = args_separated[0] 89 return type_no_constructor, constructor 90 91 92def get_font_size_constant(properties): 93 return 'kFontLayer' + properties['font'].capitalize() 94 95 96def is_graph_type(type): 97 return type == 'RunningGraph' or type == 'RunningHistogram' 98 99 100def is_text_type(type): 101 return not is_graph_type(type) 102 103 104class OverlayWidget: 105 106 def __init__(self, properties, is_graph_description=False): 107 if not is_graph_description: 108 self.name = properties['name'] 109 self.type, self.constructor = extract_type_and_constructor(properties) 110 self.extract_common(properties) 111 112 if is_graph_type(self.type): 113 description_properties = properties['description'] 114 description_properties['type'] = 'Text' 115 self.description = OverlayWidget(description_properties, True) 116 117 def extract_common(self, properties): 118 self.color = properties['color'] 119 self.coords = properties['coords'] 120 if is_graph_type(self.type): 121 self.bar_width = properties['bar_width'] 122 self.height = properties['height'] 123 else: 124 self.font = get_font_size_constant(properties) 125 self.length = properties['length'] 126 127 self.negative_alignment = [False, False] 128 129 130def is_negative_coord(coords, axis, widgets_so_far): 131 132 if isinstance(coords[axis], unicode): 133 coord_split = coords[axis].split('.') 134 # The coordinate is in the form other_widget.edge.mode 135 # We simply need to know if other_widget's coordinate is negative or not. 136 return widgets_so_far[coord_split[0]].negative_alignment[axis] 137 138 return coords[axis] < 0 139 140 141def set_alignment_flags(overlay_widget, widgets_so_far): 142 overlay_widget.negative_alignment[0] = is_negative_coord(overlay_widget.coords, 0, 143 widgets_so_far) 144 overlay_widget.negative_alignment[1] = is_negative_coord(overlay_widget.coords, 1, 145 widgets_so_far) 146 147 if is_graph_type(overlay_widget.type): 148 set_alignment_flags(overlay_widget.description, widgets_so_far) 149 150 151def get_offset_helper(widget, axis, smaller_coord_side): 152 # Assume axis is X. This function returns two values: 153 # - An offset where the bounding box is placed at, 154 # - Whether this offset is for the left or right edge. 155 # 156 # The input coordinate (widget.coord[axis]) is either: 157 # 158 # - a number: in this case, the offset is that number, and its sign determines whether this refers to the left or right edge of the bounding box. 159 # - other_widget.edge.mode: this has multiple possibilities: 160 # * edge=left, mode=align: the offset is other_widget.left, the edge is left. 161 # * edge=left, mode=adjacent: the offset is other_widget.left, the edge is right. 162 # * edge=right, mode=align: the offset is other_widget.right, the edge is right. 163 # * edge=right, mode=adjacent: the offset is other_widget.right, the edge is left. 164 # 165 # The case for the Y axis is similar, with the edge values being top or bottom. 166 167 coord = widget.coords[axis] 168 if not isinstance(coord, unicode): 169 is_left = coord >= 0 170 return coord, is_left 171 172 coord_split = coord.split('.') 173 174 is_left = coord_split[1] == smaller_coord_side 175 is_align = coord_split[2] == 'align' 176 177 other_widget_coords = 'mState.mOverlayWidgets[WidgetId::' + coord_split[0] + ']->coords' 178 other_widget_coord_index = axis + (0 if is_left else 2) 179 offset = other_widget_coords + '[' + str(other_widget_coord_index) + ']' 180 181 return offset, is_left == is_align 182 183 184def get_offset_x(widget): 185 return get_offset_helper(widget, 0, 'left') 186 187 188def get_offset_y(widget): 189 return get_offset_helper(widget, 1, 'top') 190 191 192def get_bounding_box_coords(offset, width, offset_is_left, is_left_aligned): 193 # See comment in generate_widget_init_helper. This function is implementing the following: 194 # 195 # - offset_is_left && is_left_aligned: [offset, offset + width] 196 # - offset_is_left && !is_left_aligned: [offset, std::min(offset + width, -1)] 197 # - !offset_is_left && is_left_aligned: [std::max(1, offset - width), offset] 198 # - !offset_is_left && !is_left_aligned: [offset - width, offset] 199 200 coord_left = offset if offset_is_left else (offset + ' - ' + width) 201 coord_right = (offset + ' + ' + width) if offset_is_left else offset 202 203 if offset_is_left and not is_left_aligned: 204 coord_right = 'std::min(' + coord_right + ', -1)' 205 if not offset_is_left and is_left_aligned: 206 coord_left = 'std::max(' + coord_left + ', 1)' 207 208 return coord_left, coord_right 209 210 211def generate_widget_init_helper(widget, is_graph_description=False): 212 font_size = '0' 213 214 # Common attributes 215 color = [channel / 255.0 for channel in widget.color] 216 offset_x, offset_x_is_left = get_offset_x(widget) 217 offset_y, offset_y_is_top = get_offset_y(widget) 218 219 if is_text_type(widget.type): 220 # Attributes deriven from text properties 221 font_size = widget.font 222 width = str(widget.length) + ' * kFontGlyphWidths[fontSize]' 223 height = 'kFontGlyphHeights[fontSize]' 224 else: 225 # Attributes deriven from graph properties 226 width = str(widget.bar_width) + ' * static_cast<uint32_t>(widget->runningValues.size())' 227 height = widget.height 228 229 is_left_aligned = not widget.negative_alignment[0] 230 is_top_aligned = not widget.negative_alignment[1] 231 232 # We have offset_x, offset_y, width and height which together determine the bounding box. If 233 # offset_x_is_left, the bounding box X would be in [offset_x, offset_x + width], otherwise it 234 # would be in [offset_x - width, offset_x]. Similarly for y. Since we use negative values to 235 # mean aligned to the right side of the screen, we need to make sure that: 236 # 237 # - if left aligned: offset_x - width is at minimum 1 238 # - if right aligned: offset_x + width is at maximum -1 239 # 240 # We therefore have the following combinations for the X axis: 241 # 242 # - offset_x_is_left && is_left_aligned: [offset_x, offset_x + width] 243 # - offset_x_is_left && !is_left_aligned: [offset_x, std::min(offset_x + width, -1)] 244 # - !offset_x_is_left && is_left_aligned: [std::max(1, offset_x - width), offset_x] 245 # - !offset_x_is_left && !is_left_aligned: [offset_x - width, offset_x] 246 # 247 # Similarly for y. 248 coord0, coord2 = get_bounding_box_coords('offsetX', 'width', offset_x_is_left, is_left_aligned) 249 coord1, coord3 = get_bounding_box_coords('offsetY', 'height', offset_y_is_top, is_top_aligned) 250 251 return template_init_widget.format( 252 subwidget='description.' if is_graph_description else '', 253 offset_x=offset_x, 254 offset_y=offset_y, 255 width=width, 256 height=height, 257 type=widget.type, 258 font_size=font_size, 259 coord0=coord0, 260 coord1=coord1, 261 coord2=coord2, 262 coord3=coord3, 263 color_r=color[0], 264 color_g=color[1], 265 color_b=color[2], 266 color_a=color[3]) 267 268 269def generate_widget_init(widget): 270 widget_init = '{\n' + widget.type + ' *widget = new ' + widget.constructor + ';\n' 271 272 widget_init += generate_widget_init_helper(widget) 273 widget_init += 'mState.mOverlayWidgets[WidgetId::' + widget.name + '].reset(widget);\n' 274 275 if is_graph_type(widget.type): 276 widget_init += generate_widget_init_helper(widget.description, True) 277 278 widget_init += '}\n' 279 280 return widget_init 281 282 283def main(): 284 if len(sys.argv) == 2 and sys.argv[1] == 'inputs': 285 print(in_file) 286 return 287 if len(sys.argv) == 2 and sys.argv[1] == 'outputs': 288 print(out_file) 289 return 290 291 with open(in_file) as fin: 292 layout = json.loads(fin.read()) 293 294 # Read the layouts from the json file and determine alignment of widgets (as they can refer to 295 # other widgets. 296 overlay_widgets = {} 297 for widget_properties in layout['widgets']: 298 widget = OverlayWidget(widget_properties) 299 overlay_widgets[widget.name] = widget 300 set_alignment_flags(widget, overlay_widgets) 301 302 # Go over the widgets again and generate initialization code. Note that we need to iterate over 303 # the widgets in order, so we can't use the overlay_widgets dictionary for iteration. 304 init_widgets = [] 305 for widget_properties in layout['widgets']: 306 init_widgets.append(generate_widget_init(overlay_widgets[widget_properties['name']])) 307 308 with open(out_file, 'w') as outfile: 309 outfile.write( 310 template_out_file.format( 311 script_name=__file__, 312 copyright_year=date.today().year, 313 input_file_name=in_file, 314 out_file_name=out_file, 315 init_widgets='\n'.join(init_widgets))) 316 outfile.close() 317 318 319if __name__ == '__main__': 320 sys.exit(main()) 321