openMSX
OSDGUI.cc
Go to the documentation of this file.
1 #include "OSDGUI.hh"
2 #include "OSDWidget.hh"
3 #include "OSDRectangle.hh"
4 #include "OSDText.hh"
5 #include "Display.hh"
6 #include "CommandException.hh"
7 #include "TclObject.hh"
8 #include "StringOp.hh"
9 #include "one_of.hh"
10 #include "outer.hh"
11 #include <array>
12 #include <memory>
13 #include <utility>
14 
15 namespace openmsx {
16 
17 // class OSDGUI
18 
19 OSDGUI::OSDGUI(CommandController& commandController, Display& display_)
20  : display(display_)
21  , osdCommand(commandController)
22  , topWidget(display_)
23 {
24 }
25 
26 void OSDGUI::refresh() const
27 {
28  getDisplay().repaintDelayed(40000); // 25 fps
29 }
30 
31 
32 // class OSDCommand
33 
34 OSDGUI::OSDCommand::OSDCommand(CommandController& commandController_)
35  : Command(commandController_, "osd")
36 {
37 }
38 
39 void OSDGUI::OSDCommand::execute(std::span<const TclObject> tokens, TclObject& result)
40 {
41  checkNumArgs(tokens, AtLeast{2}, "subcommand ?arg ...?");
42  executeSubCommand(tokens[1].getString(),
43  "create", [&]{ create(tokens, result); },
44  "destroy", [&]{ destroy(tokens, result); },
45  "info", [&]{ info(tokens, result); },
46  "exists", [&]{ exists(tokens, result); },
47  "configure", [&]{ configure(tokens, result); });
48 }
49 
50 void OSDGUI::OSDCommand::create(std::span<const TclObject> tokens, TclObject& result)
51 {
52  checkNumArgs(tokens, AtLeast{4}, Prefix{2}, "type name ?property value ...?");
53  std::string_view type = tokens[2].getString();
54  const auto& fullname = tokens[3];
55  auto fullnameStr = fullname.getString();
56 
57  auto& gui = OUTER(OSDGUI, osdCommand);
58  auto& top = gui.getTopWidget();
59  if (top.findByName(fullnameStr)) {
60  throw CommandException(
61  "There already exists a widget with this name: ",
62  fullnameStr);
63  }
64 
65  auto [parentname, childName] = StringOp::splitOnLast(fullnameStr, '.');
66  auto* parent = childName.empty() ? &top : top.findByName(parentname);
67  if (!parent) {
68  throw CommandException(
69  "Parent widget doesn't exist yet:", parentname);
70  }
71 
72  auto widget = create(type, fullname);
73  auto* widget2 = widget.get();
74  configure(*widget, tokens.subspan(4));
75  top.addName(*widget);
76  parent->addWidget(std::move(widget));
77 
78  result = fullname;
79  if (widget2->isVisible()) {
80  gui.refresh();
81  }
82 }
83 
84 std::unique_ptr<OSDWidget> OSDGUI::OSDCommand::create(
85  std::string_view type, const TclObject& name) const
86 {
87  auto& gui = OUTER(OSDGUI, osdCommand);
88  if (type == "rectangle") {
89  return std::make_unique<OSDRectangle>(gui.display, name);
90  } else if (type == "text") {
91  return std::make_unique<OSDText>(gui.display, name);
92  } else {
93  throw CommandException(
94  "Invalid widget type '", type, "', expected "
95  "'rectangle' or 'text'.");
96  }
97 }
98 
99 void OSDGUI::OSDCommand::destroy(std::span<const TclObject> tokens, TclObject& result)
100 {
101  checkNumArgs(tokens, 3, "name");
102  auto fullname = tokens[2].getString();
103 
104  auto& gui = OUTER(OSDGUI, osdCommand);
105  auto& top = gui.getTopWidget();
106  auto* widget = top.findByName(fullname);
107  if (!widget) {
108  // widget not found, not an error
109  result = false;
110  return;
111  }
112 
113  auto* parent = widget->getParent();
114  if (!parent) {
115  throw CommandException("Can't destroy the top widget.");
116  }
117 
118  if (widget->isVisible()) {
119  gui.refresh();
120  }
121  top.removeName(*widget);
122  parent->deleteWidget(*widget);
123  result = true;
124 }
125 
126 void OSDGUI::OSDCommand::info(std::span<const TclObject> tokens, TclObject& result)
127 {
128  checkNumArgs(tokens, Between{2, 4}, Prefix{2}, "?name? ?property?");
129  auto& gui = OUTER(OSDGUI, osdCommand);
130  switch (tokens.size()) {
131  case 2: {
132  // list widget names
133  result.addListElements(gui.getTopWidget().getAllWidgetNames());
134  break;
135  }
136  case 3: {
137  // list properties for given widget
138  const auto& widget = getWidget(tokens[2].getString());
139  result.addListElements(widget.getProperties());
140  break;
141  }
142  case 4: {
143  // get current value for given widget/property
144  const auto& widget = getWidget(tokens[2].getString());
145  widget.getProperty(tokens[3].getString(), result);
146  break;
147  }
148  }
149 }
150 
151 void OSDGUI::OSDCommand::exists(std::span<const TclObject> tokens, TclObject& result)
152 {
153  checkNumArgs(tokens, 3, "name");
154  auto& gui = OUTER(OSDGUI, osdCommand);
155  auto* widget = gui.getTopWidget().findByName(tokens[2].getString());
156  result = widget != nullptr;
157 }
158 
159 void OSDGUI::OSDCommand::configure(std::span<const TclObject> tokens, TclObject& /*result*/)
160 {
161  checkNumArgs(tokens, AtLeast{3}, "name ?property value ...?");
162  auto& widget = getWidget(tokens[2].getString());
163  configure(widget, tokens.subspan(3));
164  if (widget.isVisible()) {
165  auto& gui = OUTER(OSDGUI, osdCommand);
166  gui.refresh();
167  }
168 }
169 
170 void OSDGUI::OSDCommand::configure(OSDWidget& widget, std::span<const TclObject> tokens)
171 {
172  if (tokens.size() & 1) {
173  // odd number of extra arguments
174  throw CommandException(
175  "Missing value for '", tokens.back().getString(), "'.");
176  }
177 
178  auto& interp = getInterpreter();
179  for (size_t i = 0; i < tokens.size(); i += 2) {
180  const auto& propName = tokens[i + 0].getString();
181  widget.setProperty(interp, propName, tokens[i + 1]);
182  }
183 }
184 
185 std::string OSDGUI::OSDCommand::help(std::span<const TclObject> tokens) const
186 {
187  if (tokens.size() >= 2) {
188  if (tokens[1] == "create") {
189  return
190  "osd create <type> <widget-path> [<property-name> <property-value>]...\n"
191  "\n"
192  "Creates a new OSD widget of given type. Path is a "
193  "hierarchical name for the widget (separated by '.'). "
194  "The parent widget for this new widget must already "
195  "exist.\n"
196  "Optionally you can set initial values for one or "
197  "more properties.\n"
198  "This command returns the path of the newly created "
199  "widget. This is path is again needed to configure "
200  "or to remove the widget. It may be useful to assign "
201  "this path to a variable.";
202  } else if (tokens[1] == "destroy") {
203  return
204  "osd destroy <widget-path>\n"
205  "\n"
206  "Remove the specified OSD widget. Returns '1' on "
207  "success and '0' when widget couldn't be destroyed "
208  "because there was no widget with that name";
209  } else if (tokens[1] == "info") {
210  return
211  "osd info [<widget-path> [<property-name>]]\n"
212  "\n"
213  "Query various information about the OSD status. "
214  "You can call this command with 0, 1 or 2 arguments.\n"
215  "Without any arguments, this command returns a list "
216  "of all existing widget IDs.\n"
217  "When a path is given as argument, this command "
218  "returns a list of available properties for that widget.\n"
219  "When both path and property name arguments are "
220  "given, this command returns the current value of "
221  "that property.";
222  } else if (tokens[1] == "exists") {
223  return
224  "osd exists <widget-path>\n"
225  "\n"
226  "Test whether there exists a widget with given name. "
227  "This subcommand is meant to be used in scripts.";
228  } else if (tokens[1] == "configure") {
229  return
230  "osd configure <widget-path> [<property-name> <property-value>]...\n"
231  "\n"
232  "Modify one or more properties on the given widget.";
233  } else {
234  return "No such subcommand, see 'help osd'.";
235  }
236  } else {
237  return
238  "Low level OSD GUI commands\n"
239  " osd create <type> <widget-path> [<property-name> <property-value>]...\n"
240  " osd destroy <widget-path>\n"
241  " osd info [<widget-path> [<property-name>]]\n"
242  " osd exists <widget-path>\n"
243  " osd configure <widget-path> [<property-name> <property-value>]...\n"
244  "Use 'help osd <subcommand>' to see more info on a specific subcommand";
245  }
246 }
247 
248 void OSDGUI::OSDCommand::tabCompletion(std::vector<std::string>& tokens) const
249 {
250  using namespace std::literals;
251  auto& gui = OUTER(OSDGUI, osdCommand);
252  if (tokens.size() == 2) {
253  static constexpr std::array cmds = {
254  "create"sv, "destroy"sv, "info"sv, "exists"sv, "configure"sv
255  };
256  completeString(tokens, cmds);
257  } else if ((tokens.size() == 3) && (tokens[1] == "create")) {
258  static constexpr std::array types = {"rectangle"sv, "text"sv};
259  completeString(tokens, types);
260  } else if ((tokens.size() == 3) ||
261  ((tokens.size() == 4) && (tokens[1] == "create"))) {
262  completeString(tokens, gui.getTopWidget().getAllWidgetNames());
263  } else {
264  try {
265  auto properties = [&] {
266  if (tokens[1] == "create") {
267  auto widget = create(tokens[2], TclObject());
268  return widget->getProperties();
269  } else if (tokens[1] == one_of("configure", "info")) {
270  const auto& widget = getWidget(tokens[2]);
271  return widget.getProperties();
272  } else {
273  return std::span<const std::string_view>{};
274  }
275  }();
276  completeString(tokens, properties);
277  } catch (MSXException&) {
278  // ignore
279  }
280  }
281 }
282 
283 OSDWidget& OSDGUI::OSDCommand::getWidget(std::string_view name) const
284 {
285  auto& gui = OUTER(OSDGUI, osdCommand);
286  auto* widget = gui.getTopWidget().findByName(name);
287  if (!widget) {
288  throw CommandException("No widget with name ", name);
289  }
290  return *widget;
291 }
292 
293 } // namespace openmsx
Definition: one_of.hh:7
Represents the output window/screen of openMSX.
Definition: Display.hh:33
void repaintDelayed(uint64_t delta)
Definition: Display.cc:376
OSDGUI(CommandController &commandController, Display &display)
Definition: OSDGUI.cc:19
Display & getDisplay() const
Definition: OSDGUI.hh:18
void refresh() const
Definition: OSDGUI.cc:26
std::pair< string_view, string_view > splitOnLast(string_view str, string_view chars)
Definition: StringOp.cc:108
bool exists(zstring_view filename)
Does this file (directory) exists?
std::unique_ptr< IDEDevice > create(const DeviceConfig &config)
This file implemented 3 utility functions:
Definition: Autofire.cc:9
#define OUTER(type, member)
Definition: outer.hh:41