1 /***
2 * Created by IntelliJ IDEA.
3 * User: gareth
4 * Date: Aug 27, 2003
5 * Time: 8:34:11 AM
6 */
7 package com.tapina.robe.swi;
8
9 import com.tapina.robe.runtime.*;
10 import com.tapina.robe.swi.os.SpriteArea;
11 import com.tapina.robe.swi.wimp.*;
12 import com.tapina.robe.swi.wimp.Icon;
13 import com.tapina.robe.swi.wimp.Window;
14 import com.tapina.robe.debug.FlexWalk;
15
16 import javax.swing.*;
17 import java.awt.*;
18 import java.awt.image.BufferedImage;
19 import java.lang.reflect.Method;
20 import java.io.RandomAccessFile;
21 import java.io.IOException;
22 import java.util.Iterator;
23
24 public final class Wimp extends SWIHandler {
25 private static Wimp ourInstance;
26 private static final int WIMP_VERSION = 310;
27 private static final int WINDOW_BLOCK_SIZE = 88;
28 private static final int WINDOW_STATE_SIZE = 32;
29 private static final int ICON_BLOCK_SIZE = 32;
30
31 public synchronized static Wimp getInstance() {
32 if (ourInstance == null) {
33 ourInstance = new Wimp();
34 }
35 return ourInstance;
36 }
37
38 private Wimp() {
39 }
40
41 public static int getBase() {
42 return 0x400c0;
43 }
44
45 public String getName() {
46 return "WindowManager";
47 }
48
49 private static final String[] methodNames = new String[]{
50 "Initialise", "CreateWindow", "CreateIcon", "DeleteWindow", "DeleteIcon", "OpenWindow", "CloseWindow", "Poll",
51 "RedrawWindow", "UpdateWindow", "GetRectangle", "GetWindowState", "GetWindowInfo", "SetIconState", "GetIconState", "GetPointerInfo",
52 "DragBox", "ForceRedraw", "SetCaretPosition", "GetCaretPosition", "CreateMenu", "DecodeMenu", "WhichIcon", "SetExtent",
53 "SetPointerShape", "OpenTemplate", "CloseTemplate", "LoadTemplate", "ProcessKey", "CloseDown", "StartTask", "ReportError",
54 "GetWindowOutline", "PollIdle", "PlotIcon", "SetMode", "SetPalette", "ReadPalette", "SetColour", "SendMessage",
55 "CreateSubMenu", "SpriteOp", "BaseOfSprites", "BlockCopy", "SlotSize", "ReadPixTrans", "ClaimFreeMemory", "CommandWindow",
56 "TextColour", "TransferBlock", "ReadSysInfo", "SetFontColours", "GetMenuState", "RegisterFilter", "AddMessages", "RemoveMessages",
57 "SetColourMapping", "TextOp", "SetWatchdogState", "Extend", "ResizeIcon", "AutoScroll", "", ""
58 };
59
60 public static Method getMethod(Integer offset) throws NoSuchMethodException {
61 return getMethod(methodNames[offset.intValue()]);
62 }
63
64 private static final Method getMethod(String name) throws NoSuchMethodException {
65 return Wimp.class.getMethod(name, METHOD_PARAMETERS);
66 }
67
68 private static final class SlotSize {
69 final int current;
70 final int next;
71 final int free;
72
73 public SlotSize(int current, int free, int next) {
74 this.current = current;
75 this.free = free;
76 this.next = next;
77 }
78 }
79
80 private WimpTask wimpTask = null;
81
82 public final void Initialise(Environment env) {
83 final int[] R = env.getCpu().R;
84 if (R[1] != 0x4b534154) {
85 throw new SWIError(0, "Wimp.Initialise with incorrect magic number");
86 }
87 String taskDescription = env.getMemoryMap().getString0(R[2]);
88 final int[] acceptableMessages = new int[]{0};
89 if (R[3] != 0) {
90 if (env.getMemoryMap().getWord(R[3]) != 0) {
91 log.warning("Message filter list ignored, all messages considered important");
92 }
93 initialise(R[0], taskDescription, acceptableMessages);
94 } else {
95 initialise(R[0], taskDescription, null);
96 }
97 }
98
99 /***
100 * @param knownWimpVersion
101 * @param description
102 * @param acceptableMessages null for no acceptable messages, array containing just 0 for all messages acceptable,
103 * otherwise a list
104 */
105 public final void initialise(int knownWimpVersion, String description, int[] acceptableMessages) {
106 if (knownWimpVersion > WIMP_VERSION) {
107 throw new SWIError(0, "Wimp version requested later than current available version");
108 }
109 wimpTask = new WimpTask(knownWimpVersion, description);
110 Thread.currentThread().setName(description);
111 }
112
113 /***
114 * This tells the Wimp what the characteristics of a window are. The Wimp takes a copy of this block, so you could
115 * throw it away afterwards.
116 * @param env IN: R1 = Pointer to window block; OUT: R0 = Window handle
117 */
118 public final void CreateWindow(Environment env) {
119 final int[] R = env.getCpu().R;
120 final MemoryMap memoryMap = env.getMemoryMap();
121 final int size = Wimp.WINDOW_BLOCK_SIZE + memoryMap.getWord(R[1] + 84) * Wimp.ICON_BLOCK_SIZE;
122 final WindowBlock windowBlock = windowBlockFromTemplateData(memoryMap.getByteArray(R[1], size, false), memoryMap);
123 R[0] = createWindow(windowBlock).getWindowHandle();
124 }
125
126 public final Window createWindow(WindowBlock windowBlock) {
127 return new Window(windowBlock);
128 }
129
130 public final void CreateIcon(Environment env) {
131 final int[] R = env.getCpu().R;
132 final MemoryMap memoryMap = env.getMemoryMap();
133
134 final int blockAddress = R[1] + 4;
135 final Icon icon = iconFromTemplateData(memoryMap.getByteArray(blockAddress, ICON_BLOCK_SIZE, false), memoryMap);
136 createIcon(memoryMap.getWord(R[1]), icon);
137 }
138
139 private Icon iconFromMemory(final MemoryMap memoryMap, final int blockAddress) {
140 final int minX = memoryMap.getWord(blockAddress);
141 final int minY = memoryMap.getWord(blockAddress + 4);
142 final Rectangle boundingBox = new Rectangle(minX, minY,
143 memoryMap.getWord(blockAddress + 8) - minX,
144 memoryMap.getWord(blockAddress + 12) - minY);
145 final int iconFlags = memoryMap.getWord(blockAddress + 16);
146 final Icon icon = new Icon(boundingBox, iconFlags, (iconFlags & 0xf000) >> 12,
147 (iconFlags & 0xf0000) >> 16, (iconFlags & 0xf000000) >> 24,
148 (iconFlags & 0xf0000000) >>> 28);
149 if (icon.isTextIcon()) {
150 icon.setText("Test");
151 }
152 if (icon.isSpriteIcon()) {
153 if ((iconFlags & Icon.INDIRECTED) == 0) {
154 icon.setSprite(WIMP_SPRITE_AREA.getSprite(memoryMap.getStringN(blockAddress + 20, 12)));
155 } else {
156 if (icon.isTextIcon()) {
157 log.warning("text + sprite icons not supported");
158 } else {
159 final int pointer = memoryMap.getWord(blockAddress + 20);
160 final int spriteArea = memoryMap.getWord(blockAddress + 24);
161 final int nameLength = memoryMap.getWord(blockAddress + 28);
162 if (spriteArea == 1) {
163
164 icon.setSprite(WIMP_SPRITE_AREA.getSprite(memoryMap.getStringN(pointer, 12)));
165 } else {
166 log.warning("only named sprites from the WIMP sprite area supported");
167 }
168 }
169 }
170 }
171 return icon;
172 }
173
174 public final void createIcon(int windowHandle, Icon icon) {
175 if (windowHandle == -1) {
176 IconBar.getInstance().createIcon(icon);
177 } else {
178 log.warning("We only support creating icons in the icon bar");
179 }
180 }
181
182 /***
183 * This SWI removes the definition of the given icon. If it is the last icon in the windows list, the memory is
184 * reallocated, if not it is only marked as deleted.
185 * To update screen, you must call Wimp_ForceRedraw .
186 * @param env IN: R1 = pointer to +0 Window handle, or -2 for iconbar; +4 Icon handle
187 */
188 public final void DeleteIcon(Environment env) {
189 final int[] R = env.getCpu().R;
190 final MemoryMap memoryMap = env.getMemoryMap();
191 final int windowHandle = memoryMap.getWord(R[1]);
192 if (windowHandle == -2) {
193 log.warning("We do not remove icons from the icon bar yet");
194 } else {
195 final Window window = Window.find(windowHandle);
196 deleteIcon(window, memoryMap.getWord(R[1] + 4));
197 }
198 }
199
200 public void deleteIcon(Window window, int iconHandle) {
201 window.deleteIcon(iconHandle);
202 }
203
204 /***
205 * This call reads a windows visible state. The nested version can be used to read the windows parent.
206 * Nested version has R2 = "TASK" (&4B534154) on entry and returns R3 = parent window handle, or -1 if top level,
207 * R4 = nesting flags.
208 * @param env IN: R1 = pointer to block which contains window handle; OUT: Block in R1 contains :<pre>
209 * +0 Window Handle (or -2 for icon bar)
210 * +4 Visible area minimum x
211 * +8 Visible area minimum y
212 * +12 Visible area maximum x
213 * +16 Visible area maximum y
214 * +20 Scroll x offset
215 * +24 Scroll y offset
216 * +28 Handle to open window behind
217 * +32 Window flags</pre>
218 */
219 public void GetWindowState(Environment env) {
220 final int[] R = env.getCpu().R;
221 final MemoryMap memoryMap = env.getMemoryMap();
222 final int windowHandle = memoryMap.getWord(R[1]);
223 final Window window = getWindowState(windowHandle);
224 windowStateFromWindow(window, memoryMap.getByteArray(R[1] + 4, WINDOW_STATE_SIZE, false));
225 }
226
227 public Window getWindowState(final int windowHandle) {
228 return Window.find(windowHandle);
229 }
230
231 /***
232 * This SWI sets the icon's flags as follows : new = (old BIC clear_word) EOR EOR_word.
233 * This SWI also causes the icon to be redrawn, even if no bits are changed.
234 * @param env IN: R1 = pointer to block :<pre>
235 * 0 Window handle (-1,-2 for iconbar)
236 * 4 Icon handle
237 * 8 EOR word
238 * 12 Clear word
239 * </pre>
240 */
241 public void SetIconState(Environment env) {
242 final int[] R = env.getCpu().R;
243 final MemoryMap memoryMap = env.getMemoryMap();
244 final Window window = Window.find(memoryMap.getWord(R[1]));
245 setIconState(window.getIcon(memoryMap.getWord(R[1] + 4)), memoryMap.getWord(R[1] + 8),
246 memoryMap.getWord(R[1] + 12));
247 }
248
249 public void setIconState(Icon icon, int eorWord, int clearWord) {
250 int flags = icon.getIconFlags();
251 flags = (flags & (~clearWord)) ^ eorWord;
252 icon.setIconFlags(flags);
253 }
254
255 /***
256 * Get the icon definition for an icon.
257 * If you want to search for an icon with particular flag settings, use Wimp_WhichIcon.
258 * @param env IN: R1 = pointer to block : +0 Window handle, +4 Icon handle OUT: R0 corrupt; on exit the block will also contain +8 32-byte icon block.
259 */
260 public void GetIconState(Environment env) {
261 final int[] R = env.getCpu().R;
262 final MemoryMap memoryMap = env.getMemoryMap();
263 final int windowHandle = memoryMap.getWord(R[1]);
264 final int iconHandle = memoryMap.getWord(R[1] + 4);
265 final Window window = Window.find(windowHandle);
266 final Icon icon = getIconState(window, iconHandle);
267 iconBlockFromIcon(icon, memoryMap.getByteArray(R[1] + 8, ICON_BLOCK_SIZE, false));
268 }
269
270 private final static Icon DELETED_ICON = new Icon();
271 static {
272 DELETED_ICON.setIconFlags(Icon.DELETED);
273 }
274
275 public Icon getIconState(Window window, int iconHandle) {
276 try {
277 return window.getIcon(iconHandle);
278 } catch (IndexOutOfBoundsException e) {
279 return DELETED_ICON;
280 }
281 }
282
283 /***
284 * Get the current position of the text input caret.
285 * @param env IN: R1 = pointer to block; OUT: block contains :<pre>
286 * 0 window handle (-1 if off)
287 * 4 icon handle, or -1
288 * 8 x offset
289 * 12 y offset
290 * 16 height + flags , or -1
291 * 20 index into string, or -1
292 * Flags:
293 * 0-15 Height of caret in OS units.
294 * 16-23 Colour (if bit 26 is set)
295 * 24 1 => Use VDU5 type caret.
296 * 0 => Use anti-aliased caret.
297 * 25 1 => Caret is invisible.
298 * 26 1 => Use bits 16-23 for colour.
299 * 0 => Use wimp colour 11.
300 * 27 1 => 16-23 is untranslated col.
301 * 0 => 16-23 is wimp colour.
302 * </pre>
303 * @todo Write this method
304 */
305 public final void GetCaretPosition(Environment env) {
306 final MemoryMap memoryMap = env.getMemoryMap();
307 final int blockAddress = env.getCpu().R[1];
308 final CaretPosition caretPosition = getCaretPosition();
309 final Window window = caretPosition.getWindow();
310 memoryMap.storeWord(blockAddress, window == null? -1 : window.getWindowHandle());
311 memoryMap.storeWord(blockAddress + 4, caretPosition.getIconHandle());
312 memoryMap.storePoint(blockAddress + 8, caretPosition.getOffset());
313 memoryMap.storeWord(blockAddress + 16, window == null? -1 : caretPosition.getHeight() | (caretPosition.getColour() << 16) | caretPosition.getFlags());
314 memoryMap.storeWord(blockAddress + 20, caretPosition.getIndex());
315 }
316
317 /***
318 * @todo Need to populate the caret position with real data
319 */
320 public final CaretPosition getCaretPosition() {
321 final KeyboardFocusManager focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
322 final java.awt.Window jWindow = focusManager.getFocusedWindow();
323 final java.awt.Component jComponent = focusManager.getFocusOwner();
324 final CaretPosition caretPosition = new CaretPosition(null, -1);
325 if (jWindow != null) {
326 log.warning("Caret information is incorrect");
327 }
328 return caretPosition;
329 }
330
331 /***
332 * All icons with (icon flags AND R2) = (R3 AND R2) are added to the list in R1.
333 * The list is terminated by -1.
334 * @param env IN: R0 = Window handle, R1 = Block to contain icon handles, R2 = Bit mask, R3 = Bit settings to match
335 */
336 public final void WhichIcon(Environment env) {
337 final int[] R = env.getCpu().R;
338 final MemoryMap memoryMap = env.getMemoryMap();
339 final Window window = Window.find(R[0]);
340 final Iterator iterator = window.icons();
341 int out = R[1];
342 for (int iconHandle = 0; iterator.hasNext(); iconHandle++) {
343 Icon icon = (Icon) iterator.next();
344 if ((icon.getIconFlags() & R[2]) == (R[3] & R[2])) {
345 memoryMap.storeWord(out, iconHandle);
346 out += 4;
347 }
348 }
349 memoryMap.storeWord(out, -1);
350 }
351
352 /***
353 * Visible workarea must be wholly within new workarea extent.
354 * On RISC OS 2 workarea extent must correspond to a whole number of pixels, or strange effects may occur.
355 * Later Wimps round the values.
356 * @param env IN: R0 = window handle, R1 = pointer to block <pre>
357 * +0 new workarea min x
358 * +4 new workarea min y
359 * +8 new workarea max x
360 * +12 new workarea max y
361 * </pre>
362 */
363 public void SetExtent(Environment env) {
364 final int[] R = env.getCpu().R;
365 final MemoryMap memoryMap = env.getMemoryMap();
366 final Window window = Window.find(R[0]);
367 final Rectangle extent = env.getMemoryMap().getRectangle(R[1]);
368 setExtent(window, extent);
369 }
370
371 public void setExtent(Window window, Rectangle extent) {
372 window.setExtent(extent);
373 }
374
375 private TemplateFile templateFile = null;
376
377 /***
378 * This call opens the said template file.
379 * Now you can do repeated calls to {@link #LoadTemplate}, and finally {@link #CloseTemplate}.
380 * Entry: R1 = pathname of template file to open; Exit: R0 corrupt
381 *
382 * @param env
383 */
384 public final void OpenTemplate(Environment env) {
385 openTemplate(env.getMemoryMap().getString0(env.getCpu().R[1]));
386 }
387
388 public final void openTemplate(String filename) {
389 try {
390 if (templateFile != null) {
391 templateFile.getFile().close();
392 }
393 templateFile = new TemplateFile(new RandomAccessFile(FilenameUtils.acornToNative(filename), "r"));
394 } catch (IOException e) {
395 throw new SWIError(0, e);
396 }
397 }
398
399 public final void CloseTemplate(Environment env) {
400 closeTemplate();
401 }
402
403 public final void closeTemplate() {
404 if (templateFile != null) {
405 try {
406 templateFile.getFile().close();
407 } catch (IOException e) {
408 throw new SWIError(0, e);
409 }
410 }
411 }
412
413 /***
414 * Note that R5 must point to a word aligned 12 byte buffer, which is used as workspace by the call.
415 * Errors will not be returned if the template is not found. Instead R6 is set to 0.
416 * Entry: R1 = pointer to userbuffer for template, or 0 to find required buffersize
417 * R2 = pointer to workspace for indirected data.
418 * R3 = pointer to end of workspace (+1?)
419 * R4 = pointer to 256-byte array for font ref, or -1 for no fonts
420 * R5 = pointer to (wildcarded) name to match (3 aligned words)
421 * R6 = position to search from, or 0 for the first call
422 * Exit: R0 corrupt
423 * R2 = pointer to remaining workspace
424 * R5 is overwritten with actual name
425 * R6 = pos of next entry, or 0 if template not found
426 * if R1 was 0, then :
427 * R1 = required size for window and icons
428 * R2 = required size for indirected data
429 * @param env
430 */
431 public final void LoadTemplate(Environment env) {
432 final int[] R = env.getCpu().R;
433 final String name = env.getMemoryMap().getStringN(R[5], 12);
434 if (R[4] != -1) {
435 log.warning("We do not handle template loading with font arrays when loading: " + name);
436 }
437 final StringBuffer nameBuffer = new StringBuffer(name);
438 byte[] template = loadTemplateData(nameBuffer, R[6]);
439 if (template != null) {
440 final int numIcons = ByteArrayUtils.getInt(template, 84);
441 final int windowBlockSize = WINDOW_BLOCK_SIZE + ICON_BLOCK_SIZE * numIcons;
442 final int indirectedDataSize = template.length - windowBlockSize;
443 if (R[1] <= 0) {
444
445 R[1] = windowBlockSize;
446 R[2] = indirectedDataSize;
447 } else {
448 ByteArray data = env.getMemoryMap().getByteArray(R[1], windowBlockSize, true);
449 System.arraycopy(template, 0, data.getArray(), data.getOffset(), windowBlockSize);
450 if (indirectedDataSize > 0) {
451 remapIndirectedData(data, numIcons, 0, R[2]);
452 env.getMemoryMap().storeBytes(R[2], template, windowBlockSize, indirectedDataSize);
453 R[2] += indirectedDataSize;
454 }
455 }
456 env.getMemoryMap().storeString0(R[5], nameBuffer.toString());
457 R[6]++;
458 } else {
459 R[6] = 0;
460 }
461 }
462
463 /***
464 * Adjust the indirected data pointers of the window block template to be rebased at a new address.
465 * The title and the icons may have indirected data, which is altered.
466 * @param template ByteArray containing template window block data.
467 * @param numIcons number of icons in window definition (read from template + 84)
468 * @param oldAddress old base address of indirected data
469 * @param newAddress new base address of indirected data
470 */
471 final void remapIndirectedData(ByteArray template, int numIcons, int oldAddress, int newAddress) {
472 final int offset = newAddress - oldAddress;
473
474 final int titleFlags = template.getWord(56);
475 remapIndirectedIconData(template, titleFlags, 72, offset);
476
477 for (int i = 0; i < numIcons; i++) {
478 final int iconBase = WINDOW_BLOCK_SIZE + i * ICON_BLOCK_SIZE;
479 final int iconFlags = template.getWord(iconBase + 16);
480 remapIndirectedIconData(template, iconFlags, iconBase + 20, offset);
481 }
482 }
483
484 /***
485 * Adjust the indirected data pointers of an icon by a specified offset.
486 * If the icon is not indirected then nothing will change.
487 * @param template ByteArray containing template window block data
488 * @param iconFlags flags of icon to be adjusted, used to check if indirection is in use
489 * @param iconData offset within template of 12 bytes of icon data to adjust
490 * @param offset positive or negative adjustment required to pointers in order to rebase indirected data.
491 */
492 void remapIndirectedIconData(ByteArray template, final int iconFlags, final int iconData, final int offset) {
493 if ((iconFlags & Icon.INDIRECTED) != 0) {
494 final int text = template.getWord(iconData);
495 final int validation = template.getWord(iconData + 4);
496 template.setInt(iconData, text + offset);
497 if (validation != 1) {
498 template.setInt(iconData + 4, validation + offset);
499 }
500 }
501 }
502
503 /***
504 * Load the named template. The name may be wildcarded in which case 'index' will determine which template is
505 * loaded - to start off with index should be 0 then incremented each time until the method returns null.
506 * @param name StringBuffer containing (possibly wildcarded) name - will be overwritten with actual name
507 * @param index index of template to load
508 * @return template matching name, at specified index, or null if not found/no more
509 */
510 public final WindowBlock loadTemplate(StringBuffer name, int index) {
511 final byte[] data = loadTemplateData(name, index);
512 if (data == null) {
513 return null;
514 }
515 final ByteArray dataSource = new ByteArray(data, 0, data.length);
516 return windowBlockFromTemplateData(dataSource, dataSource);
517 }
518
519 /***
520 * Load the named template. The name may be wildcarded in which case 'index' will determine which template is loaded
521 * - to start off with index should be 0 then incremented each time until the method returns null.
522 *
523 * @param name StringBuffer containing (possibly wildcarded) name - will be overwritten with actual name
524 * @param index index of template to load
525 * @return template matching name, at specified index, or null if not found/no more
526 */
527 private byte[] loadTemplateData(StringBuffer name, int index) {
528 if (templateFile == null) {
529 throw new SWIError(0, "No template file open");
530 }
531 try {
532 final TemplateFile.Template[] templates = templateFile.getTemplates(name.toString());
533 if (index >= templates.length) {
534 return null;
535 } else {
536 final TemplateFile.Template template = templates[index];
537 name.replace(0, name.length(), template.getName());
538 return template.load();
539 }
540 } catch (IOException e) {
541 throw new SWIError(0, e);
542 }
543 }
544
545 private final WindowBlock windowBlockFromTemplateData(ByteArray data, BinaryDataSource indirectedData) {
546 final WindowBlock windowBlock = new WindowBlock();
547 windowBlock.setVisibleArea(data.getRectangle(0));
548 windowBlock.setScrollOffset(data.getPoint(16));
549 windowBlock.setHandleOfWindowAbove(data.getWord(24));
550 windowBlock.setWindowFlags(data.getWord(28));
551 windowBlock.setTitleForegroundColour(data.getByte(32));
552 windowBlock.setTitleBackgroundColour(data.getByte(33));
553 windowBlock.setWorkAreaForegroundColour(data.getByte(34));
554 windowBlock.setWorkAreaBackgroundColour(data.getByte(35));
555 windowBlock.setScrollBarOuterColour(data.getByte(36));
556 windowBlock.setScrollBarInnerColour(data.getByte(37));
557 windowBlock.setActiveTitleBackgroundColour(data.getByte(38));
558 windowBlock.setWindowExtraFlags(data.getByte(39));
559 windowBlock.setWorkArea(data.getRectangle(40));
560 windowBlock.setTitleBarIconFlags(data.getWord(56));
561 windowBlock.setWorkAreaButtonFlags(data.getWord(60));
562 final int spriteArea = data.getWord(64);
563 if (spriteArea != 1) {
564 log.warning("We always use the WIMP sprite area for windows");
565 }
566 windowBlock.setSpriteArea(WIMP_SPRITE_AREA);
567 windowBlock.setMinimumSize(new Dimension(data.getWord(68) & 0xffff, (data.getWord(68) & 0xffff0000) >>> 16));
568 if ((windowBlock.getTitleBarIconFlags() & Icon.INDIRECTED) != 0) {
569 final int address = data.getWord(72);
570 windowBlock.setTitleText(indirectedData.getStringControlTerminated(address));
571 windowBlock.setTitleIndirectAddress(address);
572 } else {
573 windowBlock.setTitleText(data.getStringControlTerminated(72, 12));
574 }
575 int numberOfIcons = data.getWord(84);
576 for (int i = 0; i < numberOfIcons; i++) {
577 final int iconBase = WINDOW_BLOCK_SIZE + i * ICON_BLOCK_SIZE;
578 final Icon icon = iconFromTemplateData(data.getByteArray(iconBase, ICON_BLOCK_SIZE, false), indirectedData);
579 windowBlock.addIcon(icon);
580 }
581 return windowBlock;
582 }
583
584 private Icon iconFromTemplateData(ByteArray data, BinaryDataSource indirectedData) {
585 final Icon icon = new Icon();
586 icon.setBoundingBox(data.getRectangle(0));
587 final int iconFlags = data.getWord(16);
588 icon.setIconFlags(iconFlags & 0x00f00fff);
589 icon.setButtonType((iconFlags & 0xf000) >> 12);
590 icon.setSelectionGroup((iconFlags & 0xf0000) >> 16);
591 icon.setForegroundColour((iconFlags & 0xf000000) >> 24);
592 icon.setBackgroundColour((iconFlags & 0xf0000000) >>> 28);
593 if (icon.isTextIcon()) {
594 if ((iconFlags & Icon.INDIRECTED) == 0) {
595 icon.setText(data.getStringControlTerminated(20, 12));
596 } else {
597 final int address = data.getWord(20);
598 icon.setText(indirectedData.getStringControlTerminated(address, data.getWord(28)));
599 icon.setTextIndirectAddress(address);
600 }
601 }
602 if (icon.isSpriteIcon()) {
603 if ((iconFlags & Icon.INDIRECTED) == 0) {
604 icon.setSprite(WIMP_SPRITE_AREA.getSprite(data.getStringControlTerminated(20, 12)));
605 } else {
606 if (icon.isTextIcon()) {
607 final int address = data.getWord(24);
608 icon.setValidationIndirectAddress(address);
609 final String validationString = indirectedData.getStringControlTerminated(address);
610 final String[] commands = validationString.split(";");
611 for (int j = 0; j < commands.length; j++) {
612 String command = commands[j];
613 if (command.charAt(0) == 'S') {
614 final String spriteName = command.substring(1).split(",")[0];
615 icon.setSprite(WIMP_SPRITE_AREA.getSprite(spriteName));
616 break;
617 }
618 }
619 } else {
620 final int iconSpriteArea = data.getWord(24);
621 if (data.getWord(28) != 0 && iconSpriteArea == 1) {
622
623 final int address = data.getWord(20);
624 final String spriteName = indirectedData.getStringControlTerminated(address, 12);
625
626 icon.setSprite(WIMP_SPRITE_AREA.getSprite(spriteName));
627 icon.setTextIndirectAddress(address);
628 } else {
629 log.warning("only named sprites from the WIMP sprite area supported");
630 }
631 }
632 }
633 }
634 return icon;
635 }
636
637 private void windowStateFromWindow(Window window, ByteArray data) {
638 data.setRectangle(0, window.getVisibleArea());
639 data.setPoint(16, window.getScrollOffset());
640 data.setInt(24, window.getHandleOfWindowAbove());
641 data.setInt(28, window.getWindowFlags());
642 }
643
644 private void iconBlockFromIcon(Icon icon, ByteArray data) {
645 data.setRectangle(0, icon.getBoundingBox());
646 data.setInt(16, icon.getIconFlags() | (icon.getButtonType() << 12) | (icon.getSelectionGroup() << 16) |
647 (icon.getForegroundColour() << 24) | (icon.getBackgroundColour() << 28));
648 final boolean indirected = (icon.getIconFlags() & Icon.INDIRECTED) != 0;
649 if (icon.isTextIcon()) {
650 if (indirected) {
651 data.setInt(20, icon.getTextIndirectAddress());
652 } else {
653 data.setString0(20, icon.getText(), 12);
654 }
655 }
656 if (icon.isSpriteIcon()) {
657 if (indirected) {
658 if (icon.isTextIcon()) {
659 data.setInt(24, icon.getValidationIndirectAddress());
660 data.setInt(28, icon.getValidationString().length());
661 } else {
662 data.setInt(24, 1);
663 data.setInt(20, icon.getTextIndirectAddress());
664 data.setInt(28, icon.getSprite().getName().length());
665 }
666 } else {
667 data.setString0(20, icon.getSprite().getName(), 12);
668 }
669 }
670 }
671
672 /***
673 * This SWI opens a standard, non-multitasking, errorbox on the screen. Avoid, if possible. It is boring and, as
674 * stated above, non-multitasking. However, the RO3.5 extensions make the use of error boxes much more appealing to
675 * the programmer. Entry: R0 = pointer to standard SWI errorblock R1 = flags (1 = OK, 2 cancel, 4 = default cancel,
676 * 8 = no prompt, 16 = no "Error from App:", 32 = return immediately, 64 = pretend click, 128 = silent, 256 = use
677 * categories bits 9-11 = category, bits 24-27 = Continue value, bits 28-31 = Quit value) Categories are: 1=info,
678 * 2=warning, 3=program msg, 4=question, 5=user1, 6=user2, 7=reserved R2 = pointer to application name (or title if
679 * bit 4 set), or 0 for simply 'Error' R3 = sprite name (if bit 8 set) R4 = sprite area (if bit 8 set) R5 = pointer
680 * to extra buttons string (if bit 8 set) Exit: R1 = 0 if nothing selected, 1 if OK selected and 2 if Cancel, 3-5 if
681 * extra buttons Return gives default, Escape the alternate R0 corrupt
682 *
683 * @param env
684 */
685 public final void ReportError(Environment env) {
686 final int[] R = env.getCpu().R;
687 final MemoryMap memoryMap = env.getMemoryMap();
688 final int errorNumber = memoryMap.getWord(R[0]);
689 final String errorMessage = memoryMap.getString0(R[0] + 4);
690 final SWIError swiError = new SWIError(errorNumber, errorMessage);
691 final String title;
692 if (R[2] == 0) {
693 title = "Error";
694 } else if ((R[1] & 16) != 16) {
695 title = "Error from " + memoryMap.getString0(R[2]);
696 } else {
697 title = memoryMap.getString0(R[2]);
698 }
699 try {
700 FlexWalk.walk(env, 0x50000000);
701 } catch (IOException e) {
702 e.printStackTrace();
703 }
704 R[1] = reportError(swiError, title);
705 }
706
707 public final int reportError(SWIError err, String title) {
708 JFrame f;
709 log.warning("Wimp.reportError does not pay any attention to the options specified");
710 log.info("reportError: " + err.getMessage());
711 JOptionPane.showMessageDialog(f = new JFrame(), err.getMessage(), title, JOptionPane.ERROR_MESSAGE);
712 f.dispose();
713 return 0;
714 }
715
716 /***
717 * This SWI returns the current Wimp palette settings, in the form &B0G0R0LL, where L is the logical colour number
718 * used for the Wimp colour 0 to 15 (or %TTGGGGGG in 256 colour modes). Note that when even when the values are
719 * masked to give &B0R0G000 this is not correct for passing to ColourTrans. If 'TRUE' is used, then the true colour
720 * values are returned.
721 *
722 * @param env IN: R1 = pointer to 20-word palette block, R2 = "TRUE" (&45555254) to return true colour; OUT: R0
723 * corrupt, block contains palette definitions
724 */
725 public final void ReadPalette(Environment env) {
726 final int[] R = env.getCpu().R;
727 final int TRUE = 0x45555254;
728 final boolean trueColours = R[2] == TRUE;
729 final ByteArray block = env.getMemoryMap().getByteArray(R[1], 80, false);
730 final Color[] palette = readPalette();
731 for (int i = 0; i < palette.length; i++) {
732 final int bbggrr00 = ColourUtils.nativeToAcorn(palette[i]);
733 block.setInt(i * 4, trueColours? bbggrr00 : ((bbggrr00 & 0xf0f0f000) | i));
734 }
735 }
736
737 /***
738 * Sixteen Wimp colours, border colour and pointer colours 0-2 makes 20 colours altogether.
739 * Colours are formatted &BBGGRR00.
740 */
741 private Color[] palette = new Color[] {
742 Color.BLACK, Color.DARK_GRAY, Color.DARK_GRAY, Color.GRAY,
743 Color.GRAY, Color.LIGHT_GRAY, Color.LIGHT_GRAY, Color.WHITE,
744 Color.RED, Color.GREEN, Color.YELLOW, Color.BLUE,
745 Color.MAGENTA, Color.CYAN, Color.PINK, Color.ORANGE,
746 Color.BLACK, Color.BLUE, Color.CYAN, Color.RED
747 };
748
749 public Color[] readPalette() {
750 return palette;
751 }
752
753 private final static SpriteArea WIMP_SPRITE_AREA = new SpriteArea(0x400000);
754
755 /***
756 * This call is mapped to OS_SpriteOp.
757 * &100 is added to reason code in R0, and R1 is set to the Wimp sprite-area pointer.
758 * @param env IN: R0 = reason code (0..&FF), R1 unused, R2 = pointer to sprite name, R3.. as for OS_SpriteOp; OUT: as for OS_SpriteOp
759 */
760 public final void SpriteOp(Environment env) {
761 final int[] R = env.getCpu().R;
762 OS.getInstance().SpriteOp(R, WIMP_SPRITE_AREA, env.getMemoryMap().getString0(R[2]));
763 }
764
765 public final void spriteOp(int operation, String spriteName) {
766 synchronized (WIMP_SPRITE_AREA) {
767 OS.getInstance().spriteOp(operation, WIMP_SPRITE_AREA, spriteName);
768 }
769 }
770
771 public final BufferedImage spriteOp40(String spriteName) {
772 synchronized (WIMP_SPRITE_AREA) {
773 return OS.getInstance().spriteOp40(WIMP_SPRITE_AREA, spriteName);
774 }
775 }
776
777 public final void SlotSize(Environment env) {
778 final int[] R = env.getCpu().R;
779 SlotSize slotSize = getSlotSize(env.getMemoryMap());
780 if (R[0] != -1) {
781
782
783 R[0] = setSlotSize(R[0], env.getMemoryMap());
784 } else {
785 R[0] = slotSize.current;
786 }
787 if (R[1] != -1) {
788 log.warning("We do not support setting next slot size, returning success");
789 } else {
790 R[1] = slotSize.next;
791 }
792 R[2] = slotSize.free;
793 }
794
795 public int setSlotSize(final int newSize, final MemoryMap memoryMap) {
796 memoryMap.setImageSize(newSize);
797 return memoryMap.getImageSize();
798 }
799
800 public final SlotSize getSlotSize(MemoryMap mem) {
801 return new SlotSize(mem.getImageSize(), Integer.MAX_VALUE, Integer.MAX_VALUE);
802 }
803
804 public final void ReadSysInfo(Environment env) {
805 final int[] R = env.getCpu().R;
806 int[] results = readSysInfo(env.getMemoryMap(), R[0]);
807 System.arraycopy(results, 0, R, 0, results.length);
808 }
809
810 RawDataBlock spriteSuffixBlock = null;
811
812 private int[] readSysInfo(MemoryMap memoryMap, int itemIndex) {
813 switch (itemIndex) {
814 case 2:
815 if (spriteSuffixBlock == null) {
816 spriteSuffixBlock = memoryMap.createSystemDataBlock("22");
817 }
818 return new int[]{spriteSuffixBlock.getAddress()};
819 case 3:
820 return new int[]{(wimpTask == null) ? 0 : 1};
821 case 5:
822 if (wimpTask == null) {
823 return new int[]{0, 0};
824 } else {
825 return new int[]{wimpTask.getTaskHandle(), wimpTask.getKnownWimpVersion()};
826 }
827 case 7:
828 return new int[]{WIMP_VERSION};
829 default:
830 log.warning("Unknown Wimp.ReadSysInfo " + itemIndex);
831 throw new SWIError(0, "Unknown Wimp.ReadSysInfo " + itemIndex);
832 }
833 }
834 }
835