Compare commits

...

128 Commits

Author SHA1 Message Date
Saveliy Skresanov
6ce8e10adb Add a limit on the number of SPARK, LFLARE and FLARE effects drawn in one frame. 2024-04-22 22:49:00 +07:00
jacob1
47384c5572
ICE contains arbitrary elements in ctype as well and should track this in saves 2024-04-21 00:11:55 -04:00
jacob1
de345a85a1
Fix "missing custom elements" warnings when loading saves with RSST/RSSS; mark SNOW as carrying ctype as well 2024-04-19 23:31:20 -04:00
Tamás Bálint Misius
6de252eb34
Check starcatcher credentials early
It's infuriating when the entire workflow fails at the publish stage because Discord pretends to have markdown.
2024-04-14 20:36:05 +02:00
Tamás Bálint Misius
b39f3c7f55
Disable --backend=vs build
Someone please give microsoft engineers a lecture on how to use computers.
2024-04-14 20:34:34 +02:00
Tamás Bálint Misius
58c0ab4747
Fix more &vec[0] problems 2024-04-13 22:32:43 +02:00
Tamás Bálint Misius
73daf67c34
Add elem.PROP_BLACK back for compat
Of course it still does nothing.
2024-04-13 19:08:19 +02:00
Simon Robertshaw
3edb8c4233 Clamp window position/size to graphics surface 2024-04-12 18:14:19 +01:00
Saveliy Skresanov
351dc6ec87 Make dropdowns near the top of the screen go down. 2024-04-12 23:55:34 +07:00
Saveliy Skresanov
0cfb91ce86 Add day/week/month/year selector in the search. 2024-04-12 22:50:55 +07:00
Saveliy Skresanov
efeac4fd8a Make "being transparent to photons" a property PROP_PHOTPASS. 2024-04-10 21:48:51 +07:00
Simon Robertshaw
36619df4f4 Revert "Remove Renderer field from GraphicsFunContext"
This reverts commit 9f02999947 & 2c55a8a9d9
2024-04-07 16:31:10 +01:00
Simon Robertshaw
9f02999947 Remove Renderer field from GraphicsFunContext
- Only decorationsEnable & blackDecorations were ever read, graphics update functions shouldn't really be aware of the renderer.
 - `sim` should ideally not be there either, but `luaGraphicsWrapper` has more going on to sort out
 - Re-format previous commit with tabs instead of spaces
2024-04-07 10:00:08 +01:00
Simon Robertshaw
2c55a8a9d9 Remove Renderer field from GraphicsFunContext
- Only decorationsEnable & blackDecorations were ever read, graphics update functions shouldn't really be aware of the renderer.
 - `sim` should ideally not be there either, but `luaGraphicsWrapper` has more going on to sort out
2024-04-07 00:17:48 +01:00
Saveliy Skresanov
1a0eb73ea0 Use create_part in resist reactions, and set CarriesCtypeIn for RSSS. 2024-04-04 22:20:14 +07:00
Saveliy Skresanov
f8873debc6 Fix RSST/RSSS not respecting the CarriesCtypeIn field. Reset GEL's tmp field when it turns into RSST. 2024-04-04 22:10:58 +07:00
Tamás Bálint Misius
51f714de0f
Stop scrolling in ScrollPanels on mousedown
The goal was to let finger flicks that didn't qualify as panning commands cancel momentum scrolling. The final effect is that any click does, which is fine.
2024-04-03 13:40:20 +02:00
jacob1
e371d6345b
Update build numbers (v98.2.365) 2024-04-01 19:54:39 -04:00
jacob1
e55fc8703a
The squirrel infestation problem is mostly resolved
This reverts commits 5e8a28b946, e8c24e7e23, e8c24e7e23
2024-04-01 19:45:14 -04:00
jacob1
40e2e4a62a
Fix text cutoff issues in save title and search error message
Save title can now overlap with the vote buttons again. It doesn't look great, but reorganizing the preview view is a task for a later day. Save titles this long are rare.
2024-04-01 12:57:19 -04:00
jacob1
e9fdb254af
Update version numbers 2024-03-31 19:48:12 -04:00
jacob1
5e8a28b946
Option to set the max amount of sounds that will play at once, between 0-999 2024-03-31 19:42:28 -04:00
jacob1
a53595ce68
coolcats.patch (patch courtesy of Simon) 2024-03-31 11:20:08 -04:00
catsoften
e8c24e7e23
Add squir... sound. Add sound 2024-03-30 22:33:13 -04:00
jacob1
7edc413cdc
Update build number 2024-03-26 20:53:34 -04:00
Tamás Bálint Misius
53b9b0e286
Fix gravity zones not being drawn in some cases
Namely, gravity zones would be rendered when the wrong wall (i.e. not Gravity wall) was selected if custom tools were present in the SC_WALL menu section. This was because their rendering was tied to a tool index into the SC_WALL menu section, rather than to a tool identifier.
2024-03-25 20:54:45 +01:00
Tamás Bálint Misius
99cd354a16
Fix dialuges looping infinitely in some cases
Namely, when no completion callback is specified, they use themselves, of all things, as their completion callback, because lua_gettop returns 0 and somehow Lua is ok with that stack index and thinks it refers to the function being executed.
2024-03-25 20:49:00 +01:00
Tamás Bálint Misius
d56e8387cf
Fix crash upon selecting a stamp too quickly
SaveButton relies on the SaveFile/SaveInfo passed to it at construction being alive until its destruction, or at least while it's being ticked (while its Tick is being called). While SaveButtons are owned by views, SaveFiles/SaveInfos associated with them are owned by models, so models need to immediately notify views (their observers) of changes made to SaveFiles/SaveInfos, or otherwise prevent views (and thus SaveButtons) from accessing stale SaveFile/SaveInfo pointers.

The online save browser model (SearchModel) correctly notifies its observer (SearchView) about changes to its list of SaveInfos. The local save browser (FileBrowserActivity) is not MVC and simply either fully exits when its SaveFile list changes, or it cleans up its SaveButtons beforehand. The stamp browser model (LocalBrowerModel), however, would make changes to its SaveFile list without notifying its observer (LocalBrowserView) when selecting a stamp, so the latter might tick its SaveButtons after SaveFiles associated with them had already been cleaned up. This commit adds the missing notification.

The crash would manifest when the SaveFile associated with a stamp was accessed for the GameSave it owned to be sent off for rendering with ThumbnailRendererTask.
2024-03-24 12:48:15 +01:00
Tamás Bálint Misius
c1c1daa9e5
Allow exporting Lua symbols
Which optionally enables loading Lua shared modules from within even static TPT. Not that anyone actually needs this.

This currently can't work on Windows because DLLs there import symbols by [module name, symbol name] rather than just symbol name. One could in theory export Lua symbols from TPT (I don't know the exact MSVC hack this would require, .def files?) and place a lua51.dll next to the executable that just re-exports them, see https://learn.microsoft.com/en-us/cpp/build/reference/exports?view=msvc-170 , but I've yet to try this.
2024-03-24 08:37:38 +01:00
Tamás Bálint Misius
bfdfebc1d5
Remove unused MD5 code 2024-03-24 08:27:06 +01:00
jacob1
502df57cae
Fix local saves being sorted Z-A instead of A-Z 2024-03-23 23:41:41 -04:00
jacob1
a0ba5f5398
Fix some label cutoff and component overlap bugs
Profile Viewer: a few "Not Provided" labels were cutoff. The score labels overlapped with the scrollbar and prevented clicking on it.
Save Preview: Views label cutoff on saves with over 10M views (aka only id:2198). authorDate label cutoff on saves with really long usernames. This one I "fixed" by making it overlap again like it used to. It only affects a few 2nd and 3rd page saves.
Options UI: Fix ambient air temp label overlapping with textbox. Fix all checkbox secondary desc labels overlapping with checkbox itself. I "fixed" this by adding the checkbox after the label so that clicks take priority, rather than actually fixing the overlap.
2024-03-23 17:26:34 -04:00
jacob1
178519dbb0
Call ren->clearScreen() in renderer if save is invalid, fixes pixel garbage appearing in final thumbnail 2024-03-23 16:10:46 -04:00
Tamás Bálint Misius
bb471e63e1
Fix panels forwarding clicks from anywhere
Very similar to ab28f93753. Broken by 69e0a8b0aa where I added an extra MouseDownInside check to the OnMouseDown (used to be OnMouseClick) of every component except that of sliders AND apparently panels, great.
2024-03-18 18:05:28 +01:00
Tamás Bálint Misius
4b866c409a
Move unlisted stamps to the back
Rather than to the front. It's nice to immediately see stamps that you haven't seen in years but it's even nicer if your most recently used stamps stay easily accessible.
2024-03-18 12:25:14 +01:00
jacob1
3fb356e2a9
Fix renderer crashing due to SimulationData not being instantiated early enough 2024-03-17 23:05:31 -04:00
jacob1
8452d96bf6
Fix update prompt showing up when no update is available if logged out and using the beta channel 2024-03-17 18:16:53 -04:00
jacob1
eef8943a3c
Fix large screen prompt not showing on first run
desktopWidth/desktopHeight were initialized only at the end of SDLOpen, after we checked if larger scales were supported. Need to initialize the window first, then recreate the window after if the scale can be larger. The window won't be centered, but otherwise it works.
2024-03-17 18:15:54 -04:00
jacob1
5160f4ad33
Update version numbers and README 2024-03-17 16:29:14 -04:00
jacob1
7870ec56a3
Limit saves with ETRD with .tmp or .tmp2 set to 98.0 2024-03-17 16:13:56 -04:00
jacob1
16f50b808d
Add limit onto various textboxes, now that textbox width no longer artificially limits it
Save Name is limited to 50 characters by the db column, so stop it there
Ambient Air Temp glitches out when it's too long, so limit to 9
Limit profile location to 40 to prevent spam / abuse
Limit tags to 16 because that's the max the server accepts
2024-03-17 15:14:22 -04:00
jacob1
aa8ee76fbb
Saves with RSST and RSSS are now restricted to 98.0 2024-03-17 14:32:28 -04:00
jacob1
228d559ccd
Replace b_strip with strip in github actions script 2024-03-15 22:42:27 -04:00
jacob1
758d34c4eb
Snapshot 361 2024-03-15 22:12:26 -04:00
jacob1
596e6cece1
Add DECOSPACE_ constants 2024-03-13 21:22:12 -04:00
jacob1
3a3a8c1cc3
Fix save history button not working except on your own saves 2024-03-06 23:11:00 -05:00
Tamás Bálint Misius
f47d0a9828
Make emscripten snapshots use starcatcher as the backend 2024-03-06 22:06:19 +01:00
Tamás Bálint Misius
7cd684ccfe
Unhardcode powdertoy.co.uk from most places
Namely, everywhere except in the network part (first line) of the intro text, since that should be customized more extensively by a client+server developer.
2024-03-06 22:03:51 +01:00
Tamás Bálint Misius
82bcb0ef9e
Fix element buttons missing from element search
Visible since c3cd4f1691, which made element search scrollable.

The last row of buttons was only shown if it had exactly as many buttons as many fit.
2024-03-05 16:10:07 +01:00
Tamás Bálint Misius
c85ebe4a0a
Fix tooltips in element search
Visible since c3cd4f1691, which made element search scrollable.

They would behave as if the scroll panel was never actually scrolled, i.e. tooltips might be shown that belonged to buttons far outside the visible part of the panel's inner area. This was because Panel invoked the OnMouseHover of its children wrong, without taking scrolling into account.
2024-03-05 16:09:34 +01:00
Tamás Bálint Misius
ab28f93753
Fix sliders accepting clicks from anywhere
Broken by 69e0a8b0aa where I added an extra MouseDownInside check to the OnMouseDown (used to be OnMouseClick) of every component except that of sliders.
2024-03-03 19:35:12 +01:00
jacob1
a7e71db9a0
Fix mingw github builds 2024-03-01 22:31:32 -05:00
jacob1
d9172faa17
Snapshot 360 2024-03-01 22:15:38 -05:00
Tamás Bálint Misius
ef308c1e48
Fix crash if tpt.installScriptManager fails
In which case the request completion handler code neglected to reset the unique_ptr holding the request.
2024-02-29 19:04:02 +01:00
Tamás Bálint Misius
79f45eb096
Use the recommended ghactions action to set up msys2 2024-02-24 22:26:53 +01:00
jacob1
0310ac08a8
Fix potential crash when sparking ETRD 2024-02-23 10:20:39 -05:00
jacob1
ea7cd41975
compat.lua: Fix tpt.brushx, tpt.brushy, and tpt.watertest 2024-02-19 22:27:19 -05:00
jacob1
f4b836deb1
draw air before "beforesimdraw" event is fired, so that graphics work in velocity / pressure display modes 2024-02-09 23:37:12 -05:00
jacob1
a250894a12
update error message when starting font editor with no arguments 2024-02-09 23:37:11 -05:00
Tamás Bálint Misius
c3cd4f1691
Make element search scrollable
But this is very buggy, ToolButtons don't lose their hover state when they get scrolled out from under the cursor and the scroll bar ignores clicks if they land on a component under it.
2024-02-06 14:56:53 +01:00
Tamás Bálint Misius
e6e36a6b7c
Fix OnMouseEnter/Leave inside Panels
Also remove OnMouseMovedInside and the dx and dy parameters of OnMouseMoved because nothing used these for anything.
2024-02-06 14:56:53 +01:00
Tamás Bálint Misius
588fe293ec
Add panning to ScrollPanel when TouchUI is enabled 2024-02-06 14:28:59 +01:00
Tamás Bálint Misius
69e0a8b0aa
Clean up OnMouseClick/Unclick madness
This is a prerequisite for making ScrollPanel work nicely on touch screens.

Engine used the terms MouseClick and MouseUnclick to refer to events that are traditionally called MouseDown and MouseUp, this was fixed with simple renaming.

Component and friends similarly used the terms MouseClick and MouseUnclick to refer to events that are traditionally called MouseDown and MouseUp and, succumbing to their own confusing terminology, also implemented behaviours associated with both the actual events MouseDown and MouseClick in code that was responsible for handling only the actual event MouseDown (i.e. what they called MouseClick).

This had been overlooked for a long time because nobody cares that a checkbox changes state when the mouse button is pressed on it rather than when it is released.

The fix is to migrate many pieces of code that run in response to MouseDown events, such as checkbox state change code, to MouseClick events, and to redefine a MouseClick to mean a sequence of MouseDown and MouseUp inside the component, rather than just a MouseDown. This is complicated by the fact that MouseClick events report mouse coordinates relative to the top left corner of the component, while MouseDown events report them relative to the top left corner of the container of the component.

Other pieces of code that make sense to be run in response to MouseDown events, such as label selection code, were left alone.
2024-02-06 13:25:53 +01:00
Tamás Bálint Misius
ae07c55f4d
Add some sign api constants 2024-01-27 19:19:56 +01:00
Tamás Bálint Misius
09ae62ac9e
Make sure vs-env.sh actually found vs
Because of course vcvarsall.bat doesn't bother to set a nonzero exit code even if it fails.
2024-01-27 18:53:17 +01:00
Tamás Bálint Misius
622df75c29
Update some more github-maintained actions
No idea why these didn't show up before I did the previous commit.

> Node.js 16 actions are deprecated. Please update the following actions to use Node.js 20: actions/upload-artifact@v3. For more information see: https://github.blog/changelog/2023-09-22-github-actions-transitioning-from-node-16-to-node-20/.
2024-01-24 19:23:53 +01:00
Tamás Bálint Misius
cd1855fbdd
Update some github-maintained actions
> Node.js 16 actions are deprecated. Please update the following actions to use Node.js 20: actions/checkout@v3, actions/setup-python@v4. For more information see: https://github.blog/changelog/2023-09-22-github-actions-transitioning-from-node-16-to-node-20/.
2024-01-24 19:00:51 +01:00
Tamás Bálint Misius
7a10847780
Eliminate polymorphism from the CommandInterface hierarchy
This is similar to what I did to Gravity in 9068920de3. The idea is that we can choose between the implementations at compile time.
2024-01-24 18:49:34 +01:00
Tamás Bálint Misius
d87130bd66
Remove TPTScriptInterface
It was an unnecessary level in the CommandInterface hierarchy.
2024-01-24 18:09:29 +01:00
Tamás Bálint Misius
1cb8f0378f
Organize Lua API into multiple TUs 2024-01-24 11:28:27 +01:00
Tamás Bálint Misius
2a43e8aef0
Fix a deprecation warning about std::result_of_t
We still have one about std::char_traits<unsigned char> (indirectly through std::basic_string_view) though, but our lord and savouir mniip said we can migrate off of it once we're c++20.
2024-01-21 19:39:47 +01:00
Tamás Bálint Misius
3f503bcb1c
Disable custom executable manifest in vs ghactions workflows
See 1ff6a2d0ae for the original problem. The fix in that commit was a bad idea, the patch got stale and broke the previous commit.
2024-01-21 14:17:32 +01:00
Tamás Bálint Misius
a637a619c9
Disable exe icons in mingw ghactions builds
So 1ef0c1a3e0 didn't help at all, great. Either I'm terrible enough at C++ to get the same thing wrong twice, or this is a mingw bug, in which case I'm not willing to waste time trying to figure it out.
2024-01-21 13:49:26 +01:00
Tamás Bálint Misius
5839657806
Snapshot 359 2024-01-21 12:59:18 +01:00
Tamás Bálint Misius
1ef0c1a3e0
Rewrite MakeIco
There is some cursed memory problem that only ever manifests in ghactions workflows and for cursed people. I'm neither so I just rewrote the whole thing from scratch, slightly better than last time.
2024-01-21 12:57:36 +01:00
Tamás Bálint Misius
452b553350
Fix various warnings that had piled up 2024-01-21 12:46:04 +01:00
Tamás Bálint Misius
8e6faddd2f
Sanitize the Lua API
Mostly in terms of consistency of spelling (o/ou, z/s, upper/lowercase), placement (which table a function/variable is in), and getter/setter functionality (a ton of settings weren't possible to query). All of this is done, or at least is intended to be done, in a backwards-compatible manner; code that worked without errors in 97.0 should work correctly from now on also.

Also punt off duplicated and deprecated features to eventcompat.lua, renamed to just compat.lua, where they are more concisely implemented and more maintainable than on the C++ side.

Note that this means that functionality added since 97.0 is not necessarily preserved, and also that code that worked with errors may or may not keep working with errors, and it highly likely will not produce the same errors. In the future, errors coming from compat.lua should be attempted to be resolved first and foremost by migrating user code to new APIs.

List of notable, included, but not entirely relevant changes, which should probably have been done in separate commits:

 - add an enum for simulation deco spaces, there still are a few things that need such enums though
 - move clamping to Brush::SetRadius, meaning that nothing can set bogus brush sizes from now on, not even Lua
 - have LuaLuna install constructor functions in the interface table, rather than pollute _G with equivalent callable tables

The old APIs now work in accordance with existing documentation on the Wiki, though they retain weird unintended behaviour, such as accepting fewer arguments than documented. I also intend to give new APIs a thorough look later, and possibly organize C++-side code into separate TUs based on which API table it implements.

A list list of API changes follows. "status" is to be interpreted thus:

 - added: new API, the "related" API may be the limited functionality deprecated equivalent the new one is meant to extend
 - deprecated: retained for compatibility (see above), implemented in compat.lua via the "related" API, possibly meant to be removed from existing documentation, or at least very visibly marked deprecated
 - aliases: retained for compatibility (see above), implemented in compat.lua as an alias to the "related" API, possibly meant to be removed from existing documentation, or at least very visibly marked deprecated
 - renamed: not retained for compatibility because it was added after 97.0, the replacement is the "related" API
 - removed: no replacement available, see "note" for reason
 - unchanged: unchanged but noteworthy

 name                      | status     | related                 | note
---------------------------|------------|-------------------------|--------------
 Button                    | deprecated | ui.button               |
 Checkbox                  | deprecated | ui.checkbox             |
 Label                     | deprecated | ui.label                |
 ProgressBar               | deprecated | ui.progressBar          |
 Slider                    | deprecated | ui.slider               |
 Textbox                   | deprecated | ui.textbox              |
 Window                    | deprecated | ui.window               |
 bz2.COMPRESS_LIMIT        | added      | bz2.compressLimit       | identical
 bz2.COMPRESS_NOMEM        | added      | bz2.compressNomem       | identical
 bz2.DECOMPRESS_BAD        | added      | bz2.decompressBad       | identical
 bz2.DECOMPRESS_EOF        | added      | bz2.decompressEof       | identical
 bz2.DECOMPRESS_LIMIT      | added      | bz2.decompressLimit     | identical
 bz2.DECOMPRESS_NOMEM      | added      | bz2.decompressNomem     | identical
 bz2.DECOMPRESS_TYPE       | added      | bz2.decompressType      | identical
 bz2.compressLimit         | deprecated | bz2.COMPRESS_NOMEM      |
 bz2.compressNomem         | deprecated | bz2.COMPRESS_LIMIT      |
 bz2.compressOk            | removed    |                         | bz2.compress never actually returned this
 bz2.decompressBad         | deprecated | bz2.DECOMPRESS_NOMEM    |
 bz2.decompressEof         | deprecated | bz2.DECOMPRESS_LIMIT    |
 bz2.decompressLimit       | deprecated | bz2.DECOMPRESS_TYPE     |
 bz2.decompressNomem       | deprecated | bz2.DECOMPRESS_BAD      |
 bz2.decompressOk          | removed    |                         | bz2.decompress never actually returned this
 bz2.decompressType        | deprecated | bz2.DECOMPRESS_EOF      |
 elem.FLAG_MOVABLE         | deprecated | sim.FLAG_MOVABLE        |
 elem.FLAG_PHOTDECO        | deprecated | sim.FLAG_PHOTDECO       |
 elem.FLAG_SKIPMOVE        | deprecated | sim.FLAG_SKIPMOVE       |
 elem.FLAG_STAGNANT        | deprecated | sim.FLAG_STAGNANT       |
 elem.PROP_DRAWONCTYPE     | deprecated | 0                       |
 elem.ST_GAS               | deprecated | 0                       |
 elem.ST_LIQUID            | deprecated | 0                       |
 elem.ST_NONE              | deprecated | 0                       |
 elem.ST_SOLID             | deprecated | 0                       |
 elem.getByName            | added      | tpt.element             | only converts names to IDs
 evt.AFTERSIM              | added      | evt.aftersim            | identical
 evt.AFTERSIMDRAW          | added      | evt.aftersimdraw        | identical
 evt.BEFORESIM             | added      | evt.beforesim           | identical
 evt.BEFORESIMDRAW         | added      | evt.beforesimdraw       | identical
 evt.BLUR                  | added      | evt.blur                | identical
 evt.CLOSE                 | added      | evt.close               | identical
 evt.KEYPRESS              | added      | evt.keypress            | identical
 evt.KEYRELEASE            | added      | evt.keyrelease          | identical
 evt.MOUSEDOWN             | added      | evt.mousedown           | identical
 evt.MOUSEMOVE             | added      | evt.mousemove           | identical
 evt.MOUSEUP               | added      | evt.mouseup             | identical
 evt.MOUSEWHEEL            | added      | evt.mousewheel          | identical
 evt.TEXTEDITING           | added      | evt.textediting         | identical
 evt.TEXTINPUT             | added      | evt.textinput           | identical
 evt.TICK                  | added      | evt.tick                | identical
 evt.aftersim              | deprecated | evt.AFTERSIM            |
 evt.aftersimdraw          | deprecated | evt.AFTERSIMDRAW        |
 evt.beforesim             | deprecated | evt.BEFORESIM           |
 evt.beforesimdraw         | deprecated | evt.BEFORESIMDRAW       |
 evt.blur                  | deprecated | evt.BLUR                |
 evt.close                 | deprecated | evt.CLOSE               |
 evt.getModifiers          | added      | evt.getmodifiers        | identical
 evt.getmodifiers          | deprecated | evt.getModifiers        |
 evt.keypress              | deprecated | evt.KEYPRESS            |
 evt.keyrelease            | deprecated | evt.KEYRELEASE          |
 evt.mousedown             | deprecated | evt.MOUSEDOWN           |
 evt.mousemove             | deprecated | evt.MOUSEMOVE           |
 evt.mouseup               | deprecated | evt.MOUSEUP             |
 evt.mousewheel            | deprecated | evt.MOUSEWHEEL          |
 evt.textediting           | deprecated | evt.TEXTEDITING         |
 evt.textinput             | deprecated | evt.TEXTINPUT           |
 evt.tick                  | deprecated | evt.TICK                |
 ren.FIRE_SPARK            | added      |                         | was missing
 ren.colourMode            | deprecated | ren.colorMode           |
 ren.debugHUD              | deprecated | ren.debugHud            |
 ren.decorations           | added      | tpt.decorations_enable  | works with booleans
 ren.fireSize              | added      | tpt.setfire             | also a getter, takes intensity only
 ren.hud                   | added      | tpt.hud                 | works with booleans
 ren.useDisplayPreset      | added      | tpt.display_mode        | identical
 sim.AIR_NOUPDATE          | added      | sim.AIR_NO_UPDATE       | identical
 sim.AIR_NO_UPDATE         | renamed    | sim.AIR_NOUPDATE        |
 sim.AIR_PRESSUREOFF       | added      | sim.AIR_PRESSURE_OFF    | identical
 sim.AIR_PRESSURE_OFF      | renamed    | sim.AIR_PRESSUREOFF     |
 sim.AIR_VELOCITYOFF       | added      | sim.AIR_VELOCITY_OFF    | identical
 sim.AIR_VELOCITY_OFF      | renamed    | sim.AIR_VELOCITYOFF     |
 sim.BRUSH_CIRCLE          | added      | sim.CIRCLE_BRUSH        | identical
 sim.BRUSH_NUM             | renamed    | sim.NUM_DEFAULTBRUSHES  |
 sim.BRUSH_SQUARE          | added      | sim.SQUARE_BRUSH        | identical
 sim.BRUSH_TRIANGLE        | added      | sim.TRI_BRUSH           | identical
 sim.CIRCLE_BRUSH          | renamed    | sim.BRUSH_CIRCLE        |
 sim.FLAG_MOVABLE          | added      | elem.FLAG_MOVABLE       | identical
 sim.FLAG_PHOTDECO         | added      | elem.FLAG_PHOTDECO      | identical
 sim.FLAG_SKIPMOVE         | added      | elem.FLAG_SKIPMOVE      | identical
 sim.FLAG_STAGNANT         | added      | elem.FLAG_STAGNANT      | identical
 sim.MAX_PARTS             | added      | sim.NPART               | identical
 sim.NPART                 | renamed    | sim.MAX_PARTS           |
 sim.NUM_AIRMODES          | added      | sim.NUM_AIR_MODES       | identical
 sim.NUM_AIR_MODES         | renamed    | sim.NUM_AIRMODES        |
 sim.NUM_BRUSHES           | added      |                         | specifies the range of valid ui.brushID inputs
 sim.NUM_DEFAULTBRUSHES    | added      | sim.BRUSH_NUM           | identical
 sim.NUM_EDGEMODES         | added      | sim.NUM_EDGE_MODES      | identical
 sim.NUM_EDGE_MODES        | renamed    | sim.NUM_EDGEMODES       |
 sim.NUM_GRAVMODES         | added      | sim.NUM_GRAV_MODES      | identical
 sim.NUM_GRAV_MODES        | renamed    | sim.NUM_GRAVMODES       |
 sim.NUM_PARTS             | deprecated | sim.partCount           |
 sim.NUM_WALLS             | added      |                         | specifies the range of valid sim.wallMap inputs
 sim.SQUARE_BRUSH          | renamed    | sim.BRUSH_SQUARE        |
 sim.TRI_BRUSH             | renamed    | sim.BRUSH_TRIANGLE      |
 sim.ambientHeatSim        | added      | tpt.ambient_heat        | works with booleans
 sim.canMove               | added      | sim.can_move            | identical
 sim.can_move              | deprecated | sim.canMove             |
 sim.decoColour            | deprecated | sim.decoColor           |
 sim.decoSpace             | added      | tpt.decoSpace           | identical, but a function
 sim.elecMap               | added      | tpt.set_elecmap         | also a getter
 sim.ensureDeterminism     | unchanged  |                         | undocumented
 sim.fanVelocityX          | added      | tpt.set_wallmap         | also a getter, sets fan velocity separately
 sim.fanVelocityY          | added      | tpt.set_wallmap         | also a getter, sets fan velocity separately
 sim.frameRender           | added      | sim.framerender         | identical
 sim.framerender           | deprecated | sim.frameRender         |
 sim.golSpeedRatio         | added      | sim.gspeed              | identical
 sim.gravMap               | deprecated | various                 |
 sim.gravityField          | added      | sim.graMap              | gravity simulation output
 sim.gravityMass           | added      | sim.graMap              | also a getter, gravity simulation input
 sim.gspeed                | deprecated | sim.golSpeedRatio       |
 sim.hash                  | unchanged  |                         | undocumented
 sim.heatSim               | added      | tpt.heat                | works with booleans
 sim.neighbours            | deprecated | sim.neighbors           |
 sim.newtonianGravity      | added      | tpt.newtonian_gravity   | works with booleans
 sim.partCount             | added      | tpt.NUM_PARTS           | identical, but a function
 sim.partNeighbours        | deprecated | see sim.partNeighbors   |
 sim.paused                | added      | tpt.set_pause           | works with booleans
 sim.randomSeed            | added      | sim.randomseed          | identical, undocumented
 sim.randomseed            | renamed    | sim.randomSeed          | undocumented
 sim.resetGravityField     | added      | tpt.reset_gravity_field | identical
 sim.resetSpark            | added      | tpt.reset_spark         | identical
 sim.resetVelocity         | added      | tpt.reset_velocity      | identical
 sim.wallMap               | added      | tpt.set_wallmap         | also a getter, doesn't set fan velocity
 sim.waterEqualisation     | deprecated | sim.waterEqualization   |
 sim.waterEqualization     | unchanged  |                         | still works with ints, not worth the trouble
 socket.getTime            | added      | socket.gettime          | identical
 socket.gettime            | deprecated | socket.getTime          |
 tpt.active_menu           | deprecated | ui.activeMenu           |
 tpt.ambient_heat          | deprecated | sim.ambientHeatSim      |
 tpt.brushID               | deprecated | ui.brushID              |
 tpt.brushx                | deprecated | ui.brushRadius          |
 tpt.brushy                | deprecated | ui.brushRadius          |
 tpt.create                | deprecated | various                 |
 tpt.debug                 | added      | tpt.setdebug            | identical
 tpt.decoSpace             | deprecated | sim.decoSpace           |
 tpt.decorations_enable    | deprecated | ren.decorations         |
 tpt.delete                | deprecated | various                 |
 tpt.display_mode          | deprecated | ren.useDisplayPreset    |
 tpt.drawCap               | added      | tpt.setdrawcap          | identical
 tpt.drawline              | deprecated | gfx.drawLine            |
 tpt.drawpixel             | deprecated | gfx.drawPixel           |
 tpt.drawrect              | deprecated | gfx.drawRect            |
 tpt.drawtext              | deprecated | gfx.drawText            |
 tpt.el                    | deprecated | various                 |
 tpt.element               | deprecated | various                 |
 tpt.element_func          | deprecated | elem.property           |
 tpt.eltransition          | deprecated | various                 |
 tpt.fillrect              | deprecated | gfx.fillRect            |
 tpt.fpsCap                | added      | tpt.setfpscap           | identical
 tpt.getPartIndex          | deprecated | various                 |
 tpt.getUserName           | added      | tpt.get_name            | identical
 tpt.get_clipboard         | deprecated | plat.clipboardCopy      |
 tpt.get_elecmap           | deprecated | sim.elecMap             |
 tpt.get_name              | deprecated | tpt.getUserName         |
 tpt.get_numOfParts        | deprecated | sim.partCount           |
 tpt.get_property          | deprecated | sim.partProperty        |
 tpt.get_wallmap           | deprecated | sim.wallMap             |
 tpt.graphics_func         | deprecated | elem.property           |
 tpt.heat                  | deprecated | sim.heatSim             |
 tpt.hud                   | deprecated | ren.hud                 |
 tpt.menu_enabled          | deprecated | ui.menuEnabled          |
 tpt.mousex                | deprecated | ui.mousePosition        |
 tpt.mousey                | deprecated | ui.mousePosition        |
 tpt.newtonian_gravity     | deprecated | sim.newtonianGravity    |
 tpt.next_getPartIndex     | deprecated | various                 |
 tpt.num_menus             | deprecated | ui.numMenus             |
 tpt.parts                 | deprecated | various                 |
 tpt.perfectCircleBrush    | deprecated | ui.perfectCircleBrush   |
 tpt.reset_gravity_field   | deprecated | sim.resetGravityField   |
 tpt.reset_spark           | deprecated | sim.resetSpark          |
 tpt.reset_velocity        | deprecated | sim.resetVelocity       |
 tpt.selecteda             | deprecated | ui.activeTool           |
 tpt.selectedl             | deprecated | ui.activeTool           |
 tpt.selectedr             | deprecated | ui.activeTool           |
 tpt.selectedreplace       | deprecated | ui.activeTool           |
 tpt.set_clipboard         | deprecated | plat.clipboardPaste     |
 tpt.set_console           | deprecated | ui.console              |
 tpt.set_elecmap           | deprecated | sim.elecMap             |
 tpt.set_gravity           | deprecated | sim.gravityMass         |
 tpt.set_pause             | deprecated | sim.paused              |
 tpt.set_pressure          | deprecated | sim.pressure            |
 tpt.set_property          | deprecated | sim.partProperty        |
 tpt.set_wallmap           | deprecated | sim.wallMap             |
 tpt.setdebug              | deprecated | tpt.debug               |
 tpt.setdrawcap            | deprecated | tpt.drawCap             |
 tpt.setfire               | deprecated | ren.fireSize            |
 tpt.setfpscap             | deprecated | tpt.fpsCap              |
 tpt.setwindowsize         | deprecated | ui.windowSize           |
 tpt.start_getPartIndex    | deprecated | various                 |
 tpt.textwidth             | deprecated | gfx.textSize            |
 tpt.toggle_pause          | deprecated | sim.paused              |
 tpt.watertest             | deprecated | sim.waterEqualization   |
 ui.MOUSEUP_BLUR           | added      | ui.MOUSE_UP_BLUR        | identical
 ui.MOUSEUP_DRAWEND        | added      | ui.MOUSE_UP_DRAW_END    | identical
 ui.MOUSEUP_NORMAL         | added      | ui.MOUSE_UP_NORMAL      | identical
 ui.MOUSE_UP_BLUR          | deprecated | ui.MOUSEUP_BLUR         |
 ui.MOUSE_UP_DRAW_END      | deprecated | ui.MOUSEUP_DRAWEND      |
 ui.MOUSE_UP_NORMAL        | deprecated | ui.MOUSEUP_NORMAL       |
 ui.NUM_TOOLINDICES        | added      |                         | specifies the range of valid ui.activeTool inputs
 ui.activeMenu             | added      | tpt.active_menu         | identical
 ui.activeTool             | added      | tpt.selectedl, ...      | identical, but a function
 ui.brushID                | added      | tpt.brushID             | identical, but a function
 ui.brushRadius            | added      | tpt.brushx, tpt.brushy  | identical, but a function
 ui.button                 | added      | Slider:new              | standalone function
 ui.checkbox               | added      | Textbox:new             | standalone function
 ui.console                | added      | tpt.set_console         | works with booleans
 ui.label                  | added      | ProgressBar:new         | standalone function
 ui.menuEnabled            | added      | tpt.menu_enabled        | identical
 ui.mousePosition          | added      | tpt.mousex, tpt.mousey  | identical, but a function
 ui.numMenus               | added      | tpt.num_menus           | identical
 ui.perfectCircleBrush     | added      | tpt.perfectCircleBrush  | identical
 ui.progressBar            | added      | Window:new              | standalone function
 ui.slider                 | added      | Button:new              | standalone function
 ui.textbox                | added      | Label:new               | standalone function
 ui.window                 | added      | Checkbox:new            | standalone function
 ui.windowSize             | added      | tpt.setwindowsize       | also a getter
2024-01-21 11:30:24 +01:00
Tamás Bálint Misius
5e60b53a3b
Hopefully fix msys2 workflows
Apparently updating msys2 causes it to exit completely, great.
2024-01-20 23:35:53 +01:00
Tamás Bálint Misius
5eb5383f08
Fix Platform::ExecutableName on freebsd
Also fix release (but somehow only release) builds failing to link because execinfo is a library on freebsd.
2024-01-15 19:54:32 +01:00
Cracker1000
708d543d29
Make LDTC copy BIZR, BIZRG and BIZRG's wavelength too. (#936) 2024-01-15 07:18:16 +01:00
Tamás Bálint Misius
00602f58a0
Don't use quick_exit with emscripten
This is a temporary fix, that line of code needs to be given some consideration. Maybe a Platform:: function?
2024-01-14 21:52:07 +01:00
Tamás Bálint Misius
a2b5dddb83
Snapshot 358 2024-01-14 21:26:45 +01:00
catsoften
137e4038b6
Allow setting ETRD min and max distance with tmp and tmp2 (#883) 2024-01-14 20:51:27 +01:00
catsoften
b711b2935b
Make TESC create LIGH with same deco color (#901) 2024-01-14 20:48:50 +01:00
Tamás Bálint Misius
66a09ababd
Remove meson warning about mingw and lto
Because msys2 ucrt64 mingw seems to do lto just fine.
2024-01-13 22:51:32 +01:00
Tamás Bálint Misius
d21c23b46e
Don't export everything for nice stack traces in release builds
Because this blows up the size of the executable. This was meant to be done only with debug builds anyway. The release stack traces are all sorts of wonky anyway, no need to complicate them further.
2024-01-13 22:47:59 +01:00
Tamás Bálint Misius
45df2ba080
Auto-detect lldb client/server paths
Probably needs some tweaking before it's actually generic enough to work for everyone though. Absolutely not going to work for windows people in its current form.
2024-01-13 22:47:07 +01:00
Saveliy Skresanov
e314e99c4c Merge branch 'resist' 2024-01-13 13:47:37 +07:00
Tamás Bálint Misius
ee2e53958a
Make android/install-apk always stale
This effectively leaves the staleness checking to adb install, as it should be done: adb has insight into the state of the system, we don't. Newer versions of android can do incremental installs too, so reinstalling the same thing is essentially free.

Also invoke android/install-apk in the debug script to make sure that the app being debugged is up to date.
2024-01-12 20:11:03 +01:00
Saveliy Skresanov
c5034dd01a Fix clone producing RSST(RSST). 2024-01-12 23:34:34 +07:00
Tamás Bálint Misius
5e35ce42b5
Add android debug helper script
Needs some documentation though. Anyway, works on my machine(tm).
2024-01-12 17:16:32 +01:00
Tamás Bálint Misius
aff12209d3
Use gh cli for managing releases
Rather than my bespoke forks of the unmaintained actions that used to do this.
2024-01-12 17:05:00 +01:00
Tamás Bálint Misius
5da4c04f35
Remove THREAD_LOCAL hack, use thread_local
We dropped support for the buggy toolchains that needed this hack.
2024-01-12 17:05:00 +01:00
Tamás Bálint Misius
bc9d43bb10
Migrate mingw builds to msys2 ucrt, update tpt-libs 2024-01-12 17:05:00 +01:00
Gyo-hyeon Lee
8a7a36bb85 Add global characters
Add Latin Extended-B, IPA Extensions, Spacing Modifier Letters, Greek and Coptic, Cyrillic Supplement, Runic, Box Drawing, CJK Symbols and Punctuation

Signed-off-by: Gyo-hyeon Lee <perssphere07@outlook.com>
2024-01-11 18:30:35 +01:00
Tamás Bálint Misius
2f302fa586
Make sure the correct android SDK and platform are installed
Make sure by checking check whether android.jar from the SDK exists.
2024-01-10 08:48:32 +01:00
Tamás Bálint Misius
608f037e68
Explain platform clipboard external dependencies in OptionsView 2024-01-10 07:41:22 +01:00
Tamás Bálint Misius
d05b0093b9
Attempt to show and demangle symbols in bluescreen stack traces 2024-01-10 07:41:11 +01:00
Saveliy Skresanov
9a05e56f15 Restore old SOAP foam behaviour and make it work with nonstandard gravity. 2024-01-09 23:59:31 +07:00
jacob1
1dc641d13f
Fix signs loading in wrong position in PSv saves 2024-01-07 17:13:19 -05:00
jacob1
99566c8003
LogoutRequest - set checkStatus to true 2024-01-06 23:31:28 -05:00
Saveliy Skresanov
c352b08d86 More resist-related comments. 2024-01-02 18:23:19 +07:00
Tamás Bálint Misius
5411269fb2
Fix bluescreen stack trace not showing exception frames 2024-01-02 10:02:03 +01:00
Tamás Bálint Misius
ba056746b8
Bump macos runner version to 12
macos-11 runners took ages to finish the macos jobs because homebrew built everything from source on them.
2024-01-01 21:06:28 +01:00
Tamás Bálint Misius
98f5a56b76
Snapshot 357 2024-01-01 20:02:55 +01:00
Tamás Bálint Misius
4a99004327
Fix requests not being marked done when they fail early
They were marked done as far as Libcurl.cpp semantics went, but they weren't noticed by the Worker loop and thus weren't actually marked done with MarkDone.

Broken by 0cc179ae4e where I quietly (i.e. no mention of this in the commit message >_>) replaced curl_multi_wait with curl_multi_poll, the former of which would return after some time even if nothing happened and the latter of which doesn't. Thus, the request manager loop kept iterating, if slowly, and masked this issue: these half-baked requests ended up being noticed in the iteration of the Worker loop that came after the one during which they were marked done.

Also fix the MotD being finalized improperly in Client.
2024-01-01 17:17:43 +01:00
Tamás Bálint Misius
70f6b68fc5
Add platform clipboard option to settings 2024-01-01 12:29:03 +01:00
Tamás Bálint Misius
6160a6b7d5
Add stack trace to bluescreen on some systems
This is still complete nonsense on posix systems where bluescreen runs in a signal handler, but what can you do.
2024-01-01 09:34:44 +01:00
Saveliy Skresanov
929b7ee525 Resist and bcoal now make fuse powder. 2023-12-31 22:53:56 +07:00
jacob1
c60e87870a
Add includePressure to saveStamp as well
Also removed in a407aba087, as part of cleanup. Restoring it now that loadStamp also has this same parameter.
2023-12-30 21:10:28 -05:00
jacob1
cdc3eb8663
Fix "does not exist" showing up when loading stamps by id or filepath in console
SetLastError will always cause the error to show up in the console. Use tempfile->GetError() instead, which will return the same error message anyway unless the stamp doesn't exist.
2023-12-30 20:34:22 -05:00
jacob1
2619050824
sim.loadStamp: default includePressure to 1
It doesn't make much sense for a Lua function to vary depending on whether the user holds shift or not.

This restores the original behavior of includePressure in 6931f35520, which was accidentally removed later in a407aba087. Since I doubt anyone used this parameter, I'll just keep it at position 6 instead of moving it back to 4.
2023-12-30 19:46:12 -05:00
jacob1
a471a0de1f
Fix http.post using wrong argument as POST data 2023-12-30 16:03:45 -05:00
jacob1
5a762524b8
Fix header return value from HTTPRequest:finish, now returns both name and value again 2023-12-30 15:40:46 -05:00
Tamás Bálint Misius
25c65310bd
Clear stamp browser selection when page contents change 2023-12-29 14:35:59 +01:00
git4rker
7623f45e6e Implement stamp renaming 2023-12-29 10:20:55 +01:00
Tamás Bálint Misius
d84e0a0c3e
Sample properties when shift is held
Rather than when the active tool is the property tool. Solves the problem of not being able to sample "away" from the property tool when it's already selected. Also bring up the property window after each such sampling of properties.
2023-12-26 13:17:21 +01:00
Tamás Bálint Misius
c5b72b213b
Add beforesimdraw and aftersimdraw events
These fire just after RenderBegin (read: really early) and just before RenderEnd (read: really late, but before the zoom window is drawn), respectively.

Lua graphics calls now also decide whether they should draw using simulation graphics or user interface graphics based on which event is being handled. This fixes element graphics functions being unable to draw with graphics calls.
2023-12-24 13:37:54 +01:00
Saveliy Skresanov
2f25c3fe32 Merge branch 'resist' of https://github.com/The-Powder-Toy/The-Powder-Toy into resist 2023-11-26 22:11:32 +07:00
Saveliy Skresanov
bc5fad1500 Redo resist reactions. 2023-11-26 20:59:51 +07:00
Saveliy Skresanov
f5f275e657
Add descriptions for RSST and RSSS. 2023-10-08 19:33:01 +02:00
Saveliy Skresanov
15a22c6906
Add ctype mechanics. 2023-10-08 19:33:01 +02:00
Saveliy Skresanov
c4db637887
Add reactions of resist with other elements. 2023-10-08 19:33:01 +02:00
Saveliy Skresanov
4377f83184
Add liquid resist (RSST) and solid resist (RSSS). 2023-10-08 19:33:01 +02:00
Saveliy Skresanov
84b790ceaf Add descriptions for RSST and RSSS. 2023-10-09 00:20:15 +07:00
Saveliy Skresanov
78bc86d107 Add ctype mechanics. 2023-10-02 22:41:22 +07:00
Saveliy Skresanov
b000abcdd8 Add reactions of resist with other elements. 2023-10-01 21:00:53 +07:00
Saveliy Skresanov
93cbb065dd Add liquid resist (RSST) and solid resist (RSSS). 2023-09-30 20:05:29 +07:00
291 changed files with 10441 additions and 9980 deletions

108
.github/build.sh vendored
View File

@ -44,12 +44,39 @@ wasm32-emscripten-emscripten-static) ;;
*) >&2 echo "configuration $BSH_HOST_ARCH-$BSH_HOST_PLATFORM-$BSH_HOST_LIBC-$BSH_STATIC_DYNAMIC is not supported" && exit 1;;
esac
if [[ $BSH_HOST_PLATFORM == android ]]; then
android_platform=android-31
if [[ -z "${JAVA_HOME_8_X64-}" ]]; then
>&2 echo "JAVA_HOME_8_X64 not set"
exit 1
fi
if [[ -z "${ANDROID_SDK_ROOT-}" ]]; then
>&2 echo "ANDROID_SDK_ROOT not set"
exit 1
fi
if [[ -z "${ANDROID_NDK_LATEST_HOME-}" ]]; then
>&2 echo "ANDROID_NDK_LATEST_HOME not set"
exit 1
fi
fi
if [[ -z ${BSH_NO_PACKAGES-} ]]; then
case $BSH_HOST_PLATFORM in
android)
(
export PATH=$ANDROID_SDK_ROOT/cmdline-tools/latest/bin:$ANDROID_SDK_ROOT/tools/bin:$PATH
sdkmanager "platforms;$android_platform"
)
;;
windows)
if [[ $BSH_BUILD_PLATFORM == linux ]] && [[ $BSH_HOST_LIBC == mingw ]]; then
sudo apt update
sudo apt install g++-mingw-w64-x86-64
if [[ $BSH_BUILD_PLATFORM-$BSH_HOST_LIBC == windows-mingw ]]; then
pacman -S --noconfirm --needed mingw-w64-ucrt-x86_64-gcc
if [[ $BSH_STATIC_DYNAMIC == static ]]; then
pacman -S --noconfirm --needed mingw-w64-ucrt-x86_64-{cmake,7zip,jq} patch
else
pacman -S --noconfirm --needed mingw-w64-ucrt-x86_64-{pkgconf,bzip2,luajit,jsoncpp,curl,SDL2,libpng,meson,fftw,jq}
fi
export PKG_CONFIG=$(which pkg-config.exe)
fi
;;
linux)
@ -87,22 +114,6 @@ function inplace_sed() {
fi
}
if [[ $BSH_HOST_PLATFORM == android ]]; then
android_platform=android-30
if [[ -z "${JAVA_HOME_8_X64-}" ]]; then
>&2 echo "JAVA_HOME_8_X64 not set"
exit 1
fi
if [[ -z "${ANDROID_SDK_ROOT-}" ]]; then
>&2 echo "ANDROID_SDK_ROOT not set"
exit 1
fi
if [[ -z "${ANDROID_NDK_LATEST_HOME-}" ]]; then
>&2 echo "ANDROID_NDK_LATEST_HOME not set"
exit 1
fi
fi
if [[ $BSH_HOST_PLATFORM-$BSH_HOST_LIBC == windows-msvc ]]; then
case $BSH_HOST_ARCH in
x86_64) vs_env_arch=x64;;
@ -188,7 +199,7 @@ meson_configure+=$'\t'-Dapp_exe=$APP_EXE
meson_configure+=$'\t'-Dapp_id=$APP_ID
meson_configure+=$'\t'-Dapp_data=$APP_DATA
meson_configure+=$'\t'-Dapp_vendor=$APP_VENDOR
meson_configure+=$'\t'-Db_strip=false
meson_configure+=$'\t'-Dstrip=false
meson_configure+=$'\t'-Db_staticpic=false
meson_configure+=$'\t'-Dmod_id=$MOD_ID
case $BSH_HOST_ARCH-$BSH_HOST_PLATFORM-$BSH_HOST_LIBC-$BSH_DEBUG_RELEASE in
@ -208,23 +219,9 @@ if [[ $PACKAGE_MODE == nolua ]]; then
fi
if [[ $PACKAGE_MODE == backendvs ]]; then
meson_configure+=$'\t'-Dbackend=vs
echo "NOTE: patching CREATEPROCESS_MANIFEST_RESOURCE_ID out of powder-res.template.rc"
echo "TODO: remove this patch once https://github.com/mesonbuild/meson/pull/12472 makes it into a release"
echo "TODO: also remove the relevant note from the building guide"
git apply <<PATCH
diff --git a/resources/powder-res.template.rc b/resources/powder-res.template.rc
index 1dc26c78..2094049f 100644
--- a/resources/powder-res.template.rc
+++ b/resources/powder-res.template.rc
@@ -7,7 +7,6 @@
IDI_ICON ICON DISCARDABLE "@ICON_EXE_ICO@"
IDI_DOC_ICON ICON DISCARDABLE "@ICON_CPS_ICO@"
-CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "@WINUTF8_XML@"
VS_VERSION_INFO VERSIONINFO
FILEVERSION @DISPLAY_VERSION_MAJOR@,@DISPLAY_VERSION_MINOR@,0,@BUILD_NUM@
PATCH
# meson 1.2.3 configures vs projects that bring their own manifest, which conflicts with ours
# TODO: remove this patch once https://github.com/mesonbuild/meson/pull/12472 makes it into a release that we can use
meson_configure+=$'\t'-Dwindows_utf8cp=false
fi
if [[ $BSH_STATIC_DYNAMIC == static ]]; then
meson_configure+=$'\t'-Dstatic=prebuilt
@ -241,6 +238,11 @@ if [[ $BSH_STATIC_DYNAMIC == static ]]; then
c_link_args+=\'-static-libstdc++\',
fi
else
if [[ "$BSH_HOST_PLATFORM-$BSH_HOST_LIBC $BSH_BUILD_PLATFORM" == "windows-mingw windows" ]]; then
meson_configure+=$'\t'-Dworkaround_elusive_bzip2=true
meson_configure+=$'\t'-Dworkaround_elusive_bzip2_include_dir=/ucrt64/include
meson_configure+=$'\t'-Dworkaround_elusive_bzip2_lib_dir=/ucrt64/lib
fi
if [[ $BSH_BUILD_PLATFORM == linux ]]; then
meson_configure+=$'\t'-Dworkaround_elusive_bzip2=true
fi
@ -288,18 +290,21 @@ if [[ $RELEASE_TYPE == snapshot ]] && [[ $MOD_ID != 0 ]]; then
fi
if [[ $RELEASE_TYPE == snapshot ]] || [[ $MOD_ID != 0 ]]; then
meson_configure+=$'\t'-Dupdate_server=starcatcher.us/TPT
if [[ $BSH_HOST_PLATFORM == emscripten ]]; then
meson_configure+=$'\t'-Dserver=tptserv.starcatcher.us
meson_configure+=$'\t'-Dstatic_server=tptserv.starcatcher.us/Static
fi
fi
if [[ $RELEASE_TYPE != dev ]]; then
meson_configure+=$'\t'-Dignore_updates=false
fi
if [[ $BSH_HOST_PLATFORM-$BSH_HOST_LIBC == windows-mingw ]]; then
if [[ $BSH_BUILD_PLATFORM == linux ]]; then
meson_configure+=$'\t'--cross-file=.github/mingw-ghactions.ini
fi
if [[ "$BSH_HOST_PLATFORM-$BSH_HOST_LIBC" == "windows-mingw" ]]; then
meson_configure+=$'\t'--cross-file=.github/mingw-ghactions.ini
# there is some mingw bug that only ever manifests on ghactions which makes MakeIco.exe use tons of memory and fail
# TODO: remove this hack once we figure out how to fix that
meson_configure+=$'\t'-Dwindows_icons=false
fi
if [[ $BSH_HOST_PLATFORM-$BSH_HOST_LIBC != windows-mingw ]] && [[ $BSH_STATIC_DYNAMIC == static ]]; then
# LTO simply doesn't work with MinGW. I have no idea why and I also don't care.
# It also has a tendency to not play well with dynamic libraries.
if [[ $BSH_DEBUG_RELEASE-$BSH_STATIC_DYNAMIC == release-static ]]; then
meson_configure+=$'\t'-Db_lto=true
fi
if [[ $BSH_HOST_PLATFORM-$BSH_HOST_ARCH == darwin-aarch64 ]]; then
@ -308,7 +313,7 @@ fi
if [[ $BSH_HOST_PLATFORM == emscripten ]]; then
meson_configure+=$'\t'--cross-file=.github/emscripten-ghactions.ini
fi
if [[ $RELEASE_TYPE == tptlibsdev ]] && ([[ $BSH_HOST_PLATFORM == windows ]] || [[ $BSH_STATIC_DYNAMIC == static ]]); then
if [[ $RELEASE_TYPE == tptlibsdev ]] && ([[ $BSH_HOST_PLATFORM-$BSH_HOST_LIBC == windows-msvc ]] || [[ $BSH_STATIC_DYNAMIC == static ]]); then
if [[ -z ${TPTLIBSREMOTE-} ]]; then
if [[ -z "${GITHUB_REPOSITORY_OWNER-}" ]]; then
>&2 echo "GITHUB_REPOSITORY_OWNER not set"
@ -318,11 +323,6 @@ if [[ $RELEASE_TYPE == tptlibsdev ]] && ([[ $BSH_HOST_PLATFORM == windows ]] ||
else
tptlibsremote=$TPTLIBSREMOTE
fi
if [[ "$BSH_HOST_ARCH-$BSH_HOST_PLATFORM-$BSH_HOST_LIBC-$BSH_STATIC_DYNAMIC $BSH_BUILD_PLATFORM" == "x86_64-windows-mingw-dynamic linux" ]]; then
>&2 echo "this configuration is not supported in tptlibsdev mode"
touch $ASSET_PATH
exit 0
fi
tptlibsbranch=$(echo $RELEASE_NAME | cut -d '-' -f 2-) # $RELEASE_NAME is tptlibsdev-BRANCH
if [[ -d build-tpt-libs ]] && [[ ${TPTLIBSRESET-} == yes ]]; then
rm -rf build-tpt-libs
@ -350,7 +350,11 @@ if [[ $RELEASE_TYPE == tptlibsdev ]] && ([[ $BSH_HOST_PLATFORM == windows ]] ||
meson_configure+=$'\t'-Dtpt_libs_vtag=$tpt_libs_vtag
fi
if [[ $BSH_HOST_PLATFORM == android ]]; then
android_platform=android-30
android_platform_jar=$ANDROID_SDK_ROOT/platforms/$android_platform/android.jar
if ! [[ -f $android_platform_jar ]]; then
>&2 echo "$android_platform_jar not found"
exit 1
fi
meson_configure+=$'\t'--cross-file=android/cross/$BSH_HOST_ARCH.ini
cat << ANDROID_INI > .github/android-ghactions.ini
[constants]
@ -361,7 +365,7 @@ andriod_sdk_build_tools = '$ANDROID_SDK_ROOT/build-tools/32.0.0'
# android_ndk_toolchain_prefix comes from the correct cross-file in ./android/cross
android_ndk_toolchain_prefix = android_ndk_toolchain_prefix
android_platform = '$android_platform'
android_platform_jar = '$ANDROID_SDK_ROOT/platforms/' + android_platform + '/android.jar'
android_platform_jar = '$android_platform_jar'
java_runtime_jar = '$JAVA_HOME_8_X64/jre/lib/rt.jar'
[binaries]

4
.github/create-release.sh vendored Executable file
View File

@ -0,0 +1,4 @@
set -euo pipefail
IFS=$'\t\n'
gh release create --draft --verify-tag --title $RELEASE_NAME $GITHUB_REF_NAME

View File

@ -2,8 +2,8 @@
prefix = 'x86_64-w64-mingw32'
[binaries]
c = prefix + '-gcc-posix'
cpp = prefix + '-g++-posix'
c = prefix + '-gcc'
cpp = prefix + '-g++'
strip = prefix + '-strip'
windres = prefix + '-windres'

35
.github/prepare.py vendored
View File

@ -17,6 +17,7 @@ match_stable = re.fullmatch(r'refs/tags/v([0-9]+)\.([0-9]+)\.([0-9]+)', ref)
match_beta = re.fullmatch(r'refs/tags/v([0-9]+)\.([0-9]+)\.([0-9]+)b', ref)
match_snapshot = re.fullmatch(r'refs/tags/snapshot-([0-9]+)', ref)
match_tptlibsdev = re.fullmatch(r'refs/heads/tptlibsdev-(.*)', ref)
match_alljobs = re.fullmatch(r'refs/heads/(.*)-alljobs', ref)
do_release = False
do_priority = 10
if event_name == 'pull_request':
@ -43,6 +44,8 @@ elif match_tptlibsdev:
else:
release_type = 'dev'
release_name = 'dev'
if match_alljobs:
do_priority = 0
do_publish = publish_hostport and do_release
set_output('release_type', release_type)
@ -100,10 +103,10 @@ for arch, platform, libc, statdyn, bplatform, runso
( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, 'nohttp', None, 'debug', 10 ),
( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, 'nolua', None, 'debug', 10 ),
( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'release', 10 ),
# ( 'x86_64', 'windows', 'mingw', 'static', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'debug', 10 ), # ubuntu-20.04 doesn't have windows TLS headers somehow and I haven't yet figured out how to get them
# ( 'x86_64', 'windows', 'mingw', 'static', 'linux', 'ubuntu-20.04', '', False, True, '.dbg', None, None, 'release', 10 ), # ubuntu-20.04 doesn't have windows TLS headers somehow and I haven't yet figured out how to get them
( 'x86_64', 'windows', 'mingw', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'debug', 10 ),
( 'x86_64', 'windows', 'mingw', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'release', 10 ),
# ( 'x86_64', 'windows', 'mingw', 'static', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'debug', 10 ), # ubuntu-20.04 doesn't have windows TLS headers somehow and I haven't yet figured out how to get them; worse, it's a different toolchain
# ( 'x86_64', 'windows', 'mingw', 'static', 'linux', 'ubuntu-20.04', '', False, True, '.dbg', None, None, 'release', 10 ), # ubuntu-20.04 doesn't have windows TLS headers somehow and I haven't yet figured out how to get them; worse, it's a different toolchain
# ( 'x86_64', 'windows', 'mingw', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'debug', 10 ), # ubuntu-20.04 doesn't have ucrt64-capable mingw >_>
# ( 'x86_64', 'windows', 'mingw', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'release', 10 ), # ubuntu-20.04 doesn't have ucrt64-capable mingw >_>
( 'x86_64', 'windows', 'mingw', 'static', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug', 0 ), # priority = 0: static debug build
( 'x86_64', 'windows', 'mingw', 'static', 'windows', 'windows-2019', '.exe', False, True, '.dbg', None, None, 'release', 10 ),
( 'x86_64', 'windows', 'mingw', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug', 10 ),
@ -111,20 +114,20 @@ for arch, platform, libc, statdyn, bplatform, runso
( 'x86_64', 'windows', 'msvc', 'static', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug', 0 ), # priority = 0: static debug build
( 'x86_64', 'windows', 'msvc', 'static', 'windows', 'windows-2019', '.exe', True, True, '.pdb', None,'x86_64-win-msvc-static', 'release', 10 ),
( 'x86_64', 'windows', 'msvc', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug', 10 ),
( 'x86_64', 'windows', 'msvc', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, 'backendvs', None, 'debug', 0 ), # priority = 0: backend=vs build
# ( 'x86_64', 'windows', 'msvc', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, 'backendvs', None, 'debug', 0 ), # priority = 0: backend=vs build
( 'x86_64', 'windows', 'msvc', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'release', 10 ),
( 'x86', 'windows', 'msvc', 'static', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug', 0 ), # priority = 0: static debug build
( 'x86', 'windows', 'msvc', 'static', 'windows', 'windows-2019', '.exe', True, True, '.pdb', None, 'i686-win-msvc-static', 'release', 10 ),
( 'x86', 'windows', 'msvc', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug', 10 ),
( 'x86', 'windows', 'msvc', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'release', 10 ),
( 'x86_64', 'darwin', 'macos', 'static', 'darwin', 'macos-11.0', '.dmg', False, False, None, 'dmg', None, 'debug', 0 ), # priority = 0: static debug build
( 'x86_64', 'darwin', 'macos', 'static', 'darwin', 'macos-11.0', '.dmg', True, True, None, 'dmg', 'x86_64-mac-gcc-static', 'release', 10 ), # I have no idea how to separate debug info on macos
( 'x86_64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-11.0', '.dmg', False, False, None, 'dmg', None, 'debug', 10 ),
( 'x86_64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-11.0', '.dmg', False, False, None, 'dmg', None, 'release', 10 ),
( 'aarch64', 'darwin', 'macos', 'static', 'darwin', 'macos-11.0', '.dmg', False, False, None, 'dmg', None, 'debug', 0 ), # priority = 0: static debug build
( 'aarch64', 'darwin', 'macos', 'static', 'darwin', 'macos-11.0', '.dmg', True, True, None, 'dmg', 'arm64-mac-gcc-static', 'release', 10 ),
# ( 'aarch64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-11.0', '.dmg', False, False, None, 'dmg', None, 'debug', 10 ), # macos-11.0 is x86_64 and I haven't yet figured out how to get homebrew to install aarch64 libs on x86_64
# ( 'aarch64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-11.0', '.dmg', False, False, None, 'dmg', None, 'release', 10 ), # macos-11.0 is x86_64 and I haven't yet figured out how to get homebrew to install aarch64 libs on x86_64
( 'x86_64', 'darwin', 'macos', 'static', 'darwin', 'macos-12', '.dmg', False, False, None, 'dmg', None, 'debug', 0 ), # priority = 0: static debug build
( 'x86_64', 'darwin', 'macos', 'static', 'darwin', 'macos-12', '.dmg', True, True, None, 'dmg', 'x86_64-mac-gcc-static', 'release', 10 ), # I have no idea how to separate debug info on macos
( 'x86_64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-12', '.dmg', False, False, None, 'dmg', None, 'debug', 10 ),
( 'x86_64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-12', '.dmg', False, False, None, 'dmg', None, 'release', 10 ),
( 'aarch64', 'darwin', 'macos', 'static', 'darwin', 'macos-12', '.dmg', False, False, None, 'dmg', None, 'debug', 0 ), # priority = 0: static debug build
( 'aarch64', 'darwin', 'macos', 'static', 'darwin', 'macos-12', '.dmg', True, True, None, 'dmg', 'arm64-mac-gcc-static', 'release', 10 ),
# ( 'aarch64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-12', '.dmg', False, False, None, 'dmg', None, 'debug', 10 ), # macos-11.0 is x86_64 and I haven't yet figured out how to get homebrew to install aarch64 libs on x86_64
# ( 'aarch64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-12', '.dmg', False, False, None, 'dmg', None, 'release', 10 ), # macos-11.0 is x86_64 and I haven't yet figured out how to get homebrew to install aarch64 libs on x86_64
( 'x86', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', False, False, None, None, None, 'debug', 0 ), # priority = 0: rarely used debug build
( 'x86', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', True, True, '.dbg', None, 'i686-and-gcc-static', 'release', 10 ),
( 'x86_64', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', False, False, None, None, None, 'debug', 0 ), # priority = 0: rarely used debug build
@ -170,6 +173,10 @@ for arch, platform, libc, statdyn, bplatform, runso
debug_asset_path = f'{app_name_slug}-{arch}.AppImage.dbg'
debug_asset_name = f'{app_name_slug}-{arch}.AppImage.dbg'
starcatcher_name = f'powder-{release_name}-{starcatcher}{suffix}'
msys2_bash = (bplatform == 'windows' and libc == 'mingw')
shell = 'bash'
if msys2_bash:
shell = 'msys2 {0}'
build_matrix.append({
'bsh_build_platform': bplatform, # part of the unique portion of the matrix
'bsh_host_arch': arch, # part of the unique portion of the matrix
@ -178,6 +185,7 @@ for arch, platform, libc, statdyn, bplatform, runso
'bsh_static_dynamic': statdyn, # part of the unique portion of the matrix
'bsh_debug_release': dbgrel, # part of the unique portion of the matrix
'runs_on': runson,
'force_msys2_bash': msys2_bash and 'yes' or 'no',
'package_suffix': suffix,
'package_mode': mode,
'publish': publish and 'yes' or 'no',
@ -188,6 +196,7 @@ for arch, platform, libc, statdyn, bplatform, runso
'debug_asset_path': debug_asset_path,
'debug_asset_name': debug_asset_name,
'job_name': job_name,
'shell': shell,
})
if publish:
publish_matrix.append({

View File

@ -13,6 +13,8 @@ chmod 660 ~/.netrc
mountpoint=ftpmnt
mkdir $mountpoint
curlftpfs "$PUBLISH_HOSTPORT" $mountpoint -o ssl,ciphers='ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-GCM-SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_8_SHA256:TLS_AES_128_CCM_SHA256'
cp $PUBLISH_FILENAME $mountpoint/${PUBLISH_DIRECTORY:-.}/
if [[ -z ${PUBLISH_ACCESSCHECK-} ]]; then
cp $PUBLISH_FILENAME $mountpoint/${PUBLISH_DIRECTORY:-.}/
fi
fusermount -u $mountpoint
rmdir $mountpoint

12
.github/upload-release-asset.sh vendored Executable file
View File

@ -0,0 +1,12 @@
set -euo pipefail
IFS=$'\t\n'
temp=.temp
mkdir $temp
cp $ASSET_PATH $temp/$ASSET_NAME
(
cd $temp
gh release upload $GITHUB_REF_NAME $ASSET_NAME
)
rm -r $temp
echo browser_download_url=https://github.com/$GITHUB_REPOSITORY/releases/download/$GITHUB_REF_NAME/$ASSET_NAME >> $GITHUB_OUTPUT

3
.github/vs-env.sh vendored Normal file → Executable file
View File

@ -24,5 +24,8 @@ IFS=$'\t\n'
for i in $(MSYS_NO_PATHCONV=1 cmd /c "$vs_install_dir\\VC\\Auxiliary\\Build\\vcvarsall.bat" $VS_ENV_PARAMS \& env \& exit /b); do
set +e
export "$i" 2>/dev/null
echo $i | grep ERROR
set -e
done
cl

View File

@ -16,7 +16,6 @@ jobs:
prepare:
runs-on: ubuntu-latest
outputs:
release_upload_url: ${{ steps.create_release.outputs.upload_url }}
do_release: ${{ steps.prepare.outputs.do_release }}
build_matrix: ${{ steps.prepare.outputs.build_matrix }}
publish_matrix: ${{ steps.prepare.outputs.publish_matrix }}
@ -32,8 +31,8 @@ jobs:
do_publish: ${{ steps.prepare.outputs.do_publish }}
steps:
- run: git config --global core.autocrlf false
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.10'
- run: python -m pip install meson==1.2.3 ninja # TODO: go back to using latest meson once https://github.com/mesonbuild/meson/pull/12544 is live
@ -42,16 +41,19 @@ jobs:
env:
PUBLISH_HOSTPORT: ${{ secrets.STARCATCHER_PUBLISH_HOSTPORT }}
GITHUB_REF: ${{ github.ref }}
- if: steps.prepare.outputs.do_publish == 'yes'
run: sudo apt update && sudo apt install curlftpfs && bash -c './.github/starcatcher-publish.sh'
env:
PUBLISH_HOSTPORT: ${{ secrets.STARCATCHER_PUBLISH_HOSTPORT }}
PUBLISH_USERNAME: ${{ secrets.STARCATCHER_PUBLISH_USERNAME }}
PUBLISH_PASSWORD: ${{ secrets.STARCATCHER_PUBLISH_PASSWORD }}
PUBLISH_ACCESSCHECK: yes
- if: steps.prepare.outputs.do_release == 'yes'
id: create_release
uses: LBPHacker/create-release@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: ${{ steps.prepare.outputs.release_name }}
draft: true
prerelease: false
RELEASE_NAME: ${{ steps.prepare.outputs.release_name }}
run: bash -c './.github/create-release.sh'
build:
runs-on: ${{ matrix.runs_on }}
name: ${{ matrix.job_name }}
@ -59,13 +61,46 @@ jobs:
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.prepare.outputs.build_matrix) }}
defaults:
run:
shell: ${{ matrix.shell }}
steps:
- if: matrix.force_msys2_bash == 'yes'
uses: msys2/setup-msys2@v2
with:
msystem: UCRT64
update: true
path-type: strict
cache: true
# this list doesn't have to mirror the one in build.sh perfectly
# but the packages listed here get cached properly and take less time to install
install: >-
git
curl
mingw-w64-ucrt-x86_64-gcc
mingw-w64-ucrt-x86_64-pkgconf
mingw-w64-ucrt-x86_64-bzip2
mingw-w64-ucrt-x86_64-luajit
mingw-w64-ucrt-x86_64-jsoncpp
mingw-w64-ucrt-x86_64-curl
mingw-w64-ucrt-x86_64-SDL2
mingw-w64-ucrt-x86_64-libpng
mingw-w64-ucrt-x86_64-meson
mingw-w64-ucrt-x86_64-python
mingw-w64-ucrt-x86_64-python-pip
mingw-w64-ucrt-x86_64-fftw
mingw-w64-ucrt-x86_64-cmake
mingw-w64-ucrt-x86_64-7zip
mingw-w64-ucrt-x86_64-jq
patch
- run: git config --global core.autocrlf false
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
- uses: actions/checkout@v4
- if: matrix.force_msys2_bash != 'yes'
uses: actions/setup-python@v5
with:
python-version: '3.10'
- run: python -m pip install meson==1.2.3 ninja # TODO: go back to using latest meson once https://github.com/mesonbuild/meson/pull/12544 is live
- if: matrix.force_msys2_bash != 'yes'
run: python -m pip install meson==1.2.3 ninja # TODO: go back to using latest meson once https://github.com/mesonbuild/meson/pull/12544 is live
- if: matrix.bsh_build_platform == 'darwin'
run: brew install bash coreutils
- run: bash -c './.github/build.sh'
@ -89,30 +124,24 @@ jobs:
PACKAGE_MODE: ${{ matrix.package_mode }}
ASSET_PATH: ${{ matrix.asset_path }}
DEBUG_ASSET_PATH: ${{ matrix.debug_asset_path }}
- uses: LBPHacker/upload-release-asset@v2 # TODO-NTL: ship licenses
if: needs.prepare.outputs.do_release == 'yes' && matrix.publish == 'yes'
- if: needs.prepare.outputs.do_release == 'yes' && matrix.publish == 'yes' # TODO-NTL: ship licenses
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.prepare.outputs.release_upload_url }}
asset_path: build/${{ matrix.asset_path }}
asset_name: ${{ matrix.asset_name }}
asset_content_type: application/zip
- uses: LBPHacker/upload-release-asset@v2
if: needs.prepare.outputs.do_release == 'yes' && matrix.publish == 'yes' && matrix.separate_debug == 'yes'
ASSET_PATH: build/${{ matrix.asset_path }}
ASSET_NAME: ${{ matrix.asset_name }}
run: bash -c './.github/upload-release-asset.sh'
- if: needs.prepare.outputs.do_release == 'yes' && matrix.publish == 'yes' && matrix.separate_debug == 'yes'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.prepare.outputs.release_upload_url }}
asset_path: build/${{ matrix.debug_asset_path }}
asset_name: ${{ matrix.debug_asset_name }}
asset_content_type: application/zip
- uses: actions/upload-artifact@v3
ASSET_PATH: build/${{ matrix.debug_asset_path }}
ASSET_NAME: ${{ matrix.debug_asset_name }}
run: bash -c './.github/upload-release-asset.sh'
- uses: actions/upload-artifact@v4
if: matrix.artifact == 'yes'
with:
path: build/${{ matrix.asset_path }}
name: ${{ matrix.asset_name }}
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
if: matrix.artifact == 'yes' && matrix.separate_debug == 'yes'
with:
path: build/${{ matrix.debug_asset_path }}
@ -126,8 +155,8 @@ jobs:
if: needs.prepare.outputs.do_publish == 'yes'
steps:
- run: git config --global core.autocrlf false
- uses: actions/checkout@v3
- uses: actions/download-artifact@v3
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: ${{ matrix.asset_name }}
- run: mv ${{ matrix.asset_path }} ${{ matrix.starcatcher_name }}
@ -144,7 +173,7 @@ jobs:
if: needs.prepare.outputs.do_publish == 'yes'
steps:
- run: git config --global core.autocrlf false
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- run: ./.github/starcatcher-release.sh
env:
RELEASE_NAME: ${{ needs.prepare.outputs.release_name }}

View File

@ -1,4 +1,4 @@
The Powder Toy - January 2023
The Powder Toy - April 2024
==========================
Get the latest version [from the Powder Toy website](https://powdertoy.co.uk/Download.html).
@ -108,6 +108,7 @@ Controls
| Shift + R | Horizontal mirror for selected area when pasting stamps |
| Ctrl + Shift + R | Vertical mirror for selected area when pasting stamps |
| R | Rotate selected area counterclockwise when pasting stamps |
| F11 | Toggle fullscreen |
Command Line
---------------------------------------------------------------------------

272
android/debug.sh Executable file
View File

@ -0,0 +1,272 @@
#!/usr/bin/env bash
# mainly based on https://www.sh-zam.com/2019/05/debugging-krita-on-android.html
set -euo pipefail
IFS=$'\n\t'
function get_buildoption() {
jq -r '.[] | select(.name == "'$1'") | .value' < meson-info/intro-buildoptions.json
}
function get_cpp_compiler() {
jq -r '.[] | select(.name == "'$1'") | .target_sources.[] | select(.language == "cpp") | .compiler.[]' < meson-info/intro-targets.json
}
# customize
default_app_id=uk.co.powdertoy.tpt
default_app_exe=powder
default_lldb_server=/opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64/lib/clang/17/lib/linux/aarch64/lldb-server
default_lldb_client=/opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/lldb.sh
in_build_site=no
if which jq >/dev/null && [[ -f meson-info/intro-buildoptions.json ]]; then
>&2 echo "[+] pwd is a build site, auto-detecting parameters"
in_build_site=yes
default_app_id=$(get_buildoption app_id)
default_app_exe=$(get_buildoption app_exe)
found_lldb=no
compiler_path=$(get_cpp_compiler $default_app_exe)
for line in $compiler_path; do
# iterate over the command array (might be for example [ "ccache", "aarch64-linux-android21-clang++" ])
if (echo $line | grep toolchains && basename $line | grep android | grep clang++) >/dev/null; then
arch=$(basename $line | cut -d '-' -f 1)
default_lldb_server=$(realpath $(dirname $line)/../lib/clang/*/lib/linux/$arch/lldb-server)
default_lldb_client=$(realpath $(dirname $line)/lldb.sh)
found_lldb=yes
fi
done
if [[ $found_lldb != yes ]]; then
>&2 echo "[-] cannot determine LLDB paths from compiler command array:"
for line in $compiler_path; do
>&2 echo "[-] - $line"
done
exit 1
fi
>&2 echo "[+] APP_ID: $default_app_id"
>&2 echo "[+] APP_EXE: $default_app_exe"
>&2 echo "[+] LLDB_SERVER: $default_lldb_server"
>&2 echo "[+] LLDB_CLIENT: $default_lldb_client"
else
>&2 echo "[+] pwd is not a build site, not auto-detecting parameters"
fi
app_id=${APP_ID:-$default_app_id}
app_exe=${APP_EXE:-$default_app_exe}
lldb_server=${LLDB_SERVER:-$default_lldb_server}
lldb_server_port=${LLDB_SERVER_PORT:-9998}
jdb_port=${JDB_PORT:-13456}
lldb_client=${LLDB_CLIENT:-$default_lldb_client}
meson=${MESON:-meson}
adb=${ADB:-adb}
jdb=${JDB:-jdb}
# don't customize unless necessary
app_activity=${APP_ACTIVITY:-PowderActivity}
lldb_server_staging=${LLDB_SERVER_STAGING:-/data/local/tmp/lldb-server}
lldb_server_remote=${LLDB_SERVER_REMOTE:-lldb-server}
pidof_retry_count=${PIDOF_RETRY_COUNT:-20}
pidof_retry_delay=${PIDOF_RETRY_DELAY:-0.1}
function check_which() {
if ! which $1 >/dev/null; then
>&2 echo "[-] can't run $1"
return 1
fi
}
function check_env() {
if ! [[ -f $lldb_server ]]; then
>&2 echo "[-] $lldb_server doesn't exist"
return 1
fi
check_which $lldb_client
check_which $adb
check_which $jdb
}
function check_adb() {
$adb shell whoami >/dev/null
}
function maybe_install_app() {
if [[ $in_build_site != yes ]]; then
return 0
fi
android_keystore=$(get_buildoption android_keystore)
android_keyalias=$(get_buildoption android_keyalias)
if [[ -z ${ANDROID_KEYSTORE_PASS-} ]]; then
>&2 echo "[-] ANDROID_KEYSTORE_PASS not set"
>&2 echo
>&2 cat << HELP
The current directory seems to be a build site, but ANDROID_KEYSTORE_PASS is not set, so android/$app_exe.apk cannot be invoked. If you don't have a keystore yet, create one with:
ANDROID_KEYSTORE_PASS=bagelsbagels keytool -genkey \\
-keystore $android_keystore \\
-alias $android_keyalias \\
-storepass:env ANDROID_KEYSTORE_PASS \\
-keypass:env ANDROID_KEYSTORE_PASS \\
-dname CN=bagels
Then try again with:
ANDROID_KEYSTORE_PASS=bagelsbagels $0
Naturally, replace bagelsbagels with an appropriate password.
HELP
exit 1
fi
>&2 echo "[+] meson compiling android/$app_exe.apk"
if ! $meson compile sign-apk; then
>&2 echo "[-] failed"
return 1
fi
>&2 echo "[+] adb installing android/$app_exe.apk"
if ! $adb install android/$app_exe.apk; then
>&2 echo "[-] failed"
return 1
fi
}
function check_debuggable() {
$adb shell run-as $app_id whoami >/dev/null
}
function find_lldb_server() {
$adb shell run-as $app_id pgrep $lldb_server_remote >/dev/null
}
function kill_lldb_server() {
if ! $adb shell run-as $app_id pkill $lldb_server_remote; then
>&2 echo "[-] failed"
return 1
fi
}
function adb_forward() {
>&2 echo "[+] adb forwarding tcp:$jdb_port jdwp:$pid"
if ! ($adb forward tcp:$jdb_port jdwp:$pid | grep $jdb_port >/dev/null); then
>&2 echo "[+] failed"
return 1
fi
}
function adb_unforward() {
$adb forward --remove tcp:$jdb_port
}
function undo_current_adb_forward() {
>&2 echo "[+] adb un-forwarding orphaned tcp:$jdb_port"
adb_unforward
}
function maybe_undo_previous_adb_forward() {
if $adb forward --list | grep tcp:$jdb_port; then
>&2 echo "[+] adb un-forwarding orphaned tcp:$jdb_port"
adb_unforward
fi
}
function maybe_kill_previous_lldb_server() {
if find_lldb_server; then
>&2 echo "[+] killing orphaned $lldb_server_remote"
kill_lldb_server
fi
}
function kill_current_lldb_server() {
>&2 echo "[+] killing $lldb_server_remote"
kill_lldb_server
}
function start_app() {
>&2 echo "[+] starting $app_id/.$app_activity"
set +e
$adb shell am start -D -n "$app_id/.$app_activity" >/dev/null
set -e
local i
local maybe_pid
local pidof_result
for ((i = 0; i <= $pidof_retry_count; i++)); do
set +e
maybe_pid=$($adb shell pidof $app_id)
pidof_result=$?
set -e
if [[ $pidof_result == 0 ]]; then
pid=$maybe_pid
break
fi
sleep $pidof_retry_delay
done
if [[ -z ${pid-} ]]; then
>&2 echo "[-] failed"
return 1
fi
echo $pid
}
function jdb_attach() {
>&2 echo "[+] attaching jdb in the background"
$jdb -attach localhost:$jdb_port >/dev/null 2>/dev/null &
disown $!
# at some point jdb exits because it doesn't have an stdin... fine by me
}
function maybe_deploy_lldb_server() {
if ! $adb shell [[ -f $lldb_server_staging ]]; then
>&2 echo "[+] $lldb_server_remote not present on host, deploying"
if ! ($adb push $lldb_server $lldb_server_staging && $adb shell chmod +x $lldb_server_staging); then
>&2 echo "[-] failed"
fi
fi
}
function start_lldb_server() {
if ! $adb shell run-as $app_id pgrep $lldb_server_remote >/dev/null; then
>&2 echo "[+] $lldb_server_remote not running on host, starting"
$adb shell run-as $app_id cp $lldb_server_staging /data/data/$app_id/$lldb_server_remote
$adb shell run-as $app_id ./$lldb_server_remote platform --server --listen "*:$lldb_server_port" >/dev/null 2>/dev/null &
disown $!
if ! $adb shell run-as $app_id pgrep $lldb_server_remote >/dev/null; then
>&2 echo "[-] failed"
return 1
fi
fi
}
function start_lldb() {
local pid=$1
>&2 echo "[+] starting $lldb_client"
local lldb_init=$(mktemp)
cat - << LLDB_INIT > $lldb_init
platform select remote-android
platform connect connect://localhost:$lldb_server_port
attach $pid
continue
LLDB_INIT
local lldb_status
set +e
$lldb_client --source $lldb_init
lldb_status=$?
set -e
>&2 echo "[+] $lldb_client exited with status $lldb_status"
rm $lldb_init
}
check_env
check_adb
maybe_install_app
check_debuggable
maybe_kill_previous_lldb_server
maybe_undo_previous_adb_forward
if [[ ${1-} == clean ]]; then
>&2 echo "[+] done"
exit 0
fi
maybe_deploy_lldb_server
start_lldb_server
pid=$(start_app)
adb_forward
jdb_attach
start_lldb $pid
kill_current_lldb_server
undo_current_adb_forward
>&2 echo "[+] done"

View File

@ -23,6 +23,3 @@ if subprocess.run([
apk_path,
]).returncode:
sys.exit(1)
with open(phony_path, 'w') as _:
pass

View File

@ -44,9 +44,6 @@ if c_compiler.get_id() in [ 'msvc' ]
host_libc = 'msvc'
elif c_compiler.get_id() in [ 'gcc' ] and host_platform == 'windows'
host_libc = 'mingw'
if get_option('b_lto')
warning('mingw does not like static + lto')
endif
elif host_platform in [ 'darwin' ]
host_libc = 'macos'
elif host_platform in [ 'emscripten' ]
@ -69,8 +66,8 @@ if static_variant != 'prebuilt' and host_platform == 'android'
warning('only prebuilt libs are supported for android')
static_variant = 'prebuilt'
endif
if static_variant == 'system' and host_platform == 'windows'
warning('no way to find system libs on windows')
if static_variant == 'system' and host_platform == 'windows' and host_libc == 'msvc'
warning('no way to find system libs for msvc on windows')
static_variant = 'prebuilt'
endif
@ -82,20 +79,19 @@ tpt_libs_static = 'none'
if static_variant == 'prebuilt'
tpt_libs_static = 'static'
endif
if static_variant == 'none' and host_platform == 'windows'
if static_variant == 'none' and host_platform == 'windows' and host_libc == 'msvc'
tpt_libs_static = 'dynamic'
endif
tpt_libs_debug = is_debug ? 'debug' : 'release'
tpt_libs_variant = '@0@-@1@-@2@-@3@'.format(host_arch, host_platform, host_libc, tpt_libs_static)
tpt_libs_vtag = get_option('tpt_libs_vtag')
if tpt_libs_vtag == ''
tpt_libs_vtag = 'v20231003175140'
tpt_libs_vtag = 'v20240112165024'
endif
if tpt_libs_static != 'none'
if tpt_libs_variant not in [
'x86_64-linux-gnu-static',
'x86_64-windows-mingw-static',
'x86_64-windows-mingw-dynamic',
'x86_64-windows-msvc-static',
'x86_64-windows-msvc-dynamic',
'x86-windows-msvc-static',
@ -329,7 +325,8 @@ if host_platform == 'windows'
if host_arch == 'x86_64'
args_ccomp_win += [ '-DZLIB_WINAPI' ]
endif
else
endif
if tpt_libs_static == 'dynamic'
foreach input_output_condition : tpt_libs.get_variable('config_dlls')
dll_input = input_output_condition[0]
dll_output = input_output_condition[1]
@ -361,9 +358,12 @@ else
ident_platform = 'UNKNOWN'
endif
project_deps = []
data_files = []
powder_deps = []
project_export_dynamic = false
subdir('src')
subdir('resources')
@ -376,9 +376,25 @@ if host_platform == 'emscripten'
'-o', app_exe + '.js', # so we get a .wasm, and a .js
]
endif
if get_option('export_lua_symbols')
if is_static and lua_variant != 'none' and not project_export_dynamic
if host_platform == 'windows'
error('Lua symbols are currently impossible to export correctly on Windows')
elif c_compiler.has_link_argument('-Wl,--export-dynamic-symbol')
project_link_args += [
'-Wl,--export-dynamic-symbol=lua_*',
'-Wl,--export-dynamic-symbol=luaL_*',
'-Wl,--export-dynamic-symbol=luaopen_*',
]
else
warning('your linker does not support -Wl,--export-dynamic-symbol so Meson will be instructed to export all symbols in order to enable loading Lua shared modules, which may blow up the size of the resulting binary')
project_export_dynamic = true
endif
endif
endif
if get_option('build_powder')
powder_deps += [
powder_deps += project_deps + [
threads_dep,
zlib_dep,
png_dep,
@ -410,6 +426,7 @@ if get_option('build_powder')
win_subsystem: is_debug ? 'console' : 'windows',
link_args: project_link_args,
dependencies: powder_deps,
export_dynamic: project_export_dynamic,
install: true,
)
endif
@ -419,7 +436,7 @@ if get_option('build_render')
if host_platform == 'emscripten'
error('render does not target emscripten')
endif
render_deps = [
render_deps = project_deps + [
threads_dep,
zlib_dep,
bzip2_dep,
@ -438,6 +455,7 @@ if get_option('build_render')
cpp_args: project_cpp_args,
link_args: render_link_args,
dependencies: render_deps,
export_dynamic: project_export_dynamic,
)
endif
@ -445,7 +463,7 @@ if get_option('build_font')
if host_platform == 'emscripten'
error('font does not target emscripten')
endif
font_deps = [
font_deps = project_deps + [
threads_dep,
zlib_dep,
png_dep,
@ -461,5 +479,6 @@ if get_option('build_font')
cpp_args: project_cpp_args,
link_args: project_link_args,
dependencies: font_deps,
export_dynamic: project_export_dynamic,
)
endif

View File

@ -40,42 +40,42 @@ option(
'display_version_major',
type: 'integer',
min: 0,
value: 97,
value: 98,
description: 'Major component of the display version, should more or less map to the MINOR version in semantic versioning'
)
option(
'display_version_minor',
type: 'integer',
min: 0,
value: 0,
value: 2,
description: 'Minor component of the display version, should more or less map to the PATCH version in semantic versioning'
)
option(
'build_num',
type: 'integer',
min: 0,
value: 356,
value: 365,
description: 'Build number, should be strictly monotonously increasing across public releases'
)
option(
'upstream_version_major',
type: 'integer',
min: 0,
value: 97,
value: 98,
description: 'Major component of the upstream display version, mod owners should not change this but merge upstream changes to it'
)
option(
'upstream_version_minor',
type: 'integer',
min: 0,
value: 0,
value: 2,
description: 'Minor component of the upstream display version, mod owners should not change this but merge upstream changes to it'
)
option(
'upstream_build_num',
type: 'integer',
min: 0,
value: 356,
value: 365,
description: 'Upstream build number, mod owners should not change this but merge upstream changes to it'
)
option(
@ -286,3 +286,28 @@ option(
value: true,
description: 'Enable platform clipboard, allows copying simulation data between different windows'
)
option(
'use_bluescreen',
type: 'combo',
choices: [ 'no', 'yes', 'auto' ],
value: 'auto',
description: 'Show blue error screen upon unhandled signals and exceptions'
)
option(
'windows_icons',
type: 'boolean',
value: true,
description: 'Add icon resources to the executable on Windows'
)
option(
'windows_utf8cp',
type: 'boolean',
value: true,
description: 'Ask Windows nicely for UTF-8 as the codepage'
)
option(
'export_lua_symbols',
type: 'boolean',
value: false,
description: 'Export Lua symbols to enable loading of Lua shared modules'
)

View File

@ -1,67 +1,137 @@
#include <fstream>
#include <vector>
#include <cstdint>
#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
static void writeU32LE(uint8_t *dest, uint32_t value)
{
dest[0] = uint8_t( value & 0xFF);
dest[1] = uint8_t((value >> 8) & 0xFF);
dest[2] = uint8_t((value >> 16) & 0xFF);
dest[3] = uint8_t((value >> 24) & 0xFF);
}
static uint32_t readU32BE(const uint8_t *src)
{
return uint32_t(src[3]) |
(uint32_t(src[2]) << 8) |
(uint32_t(src[1]) << 16) |
(uint32_t(src[0]) << 24);
}
int main(int argc, char *argv[])
{
if (argc < 3)
{
return 1;
}
auto *outputIcoPath = argv[1];
std::ofstream outputIco(outputIcoPath, std::ios::binary);
if (!outputIco)
{
return 2;
std::cerr << "usage: " << argv[0] << " OUTPUT INPUT..." << std::endl;
exit(1);
}
auto images = argc - 2;
std::vector<char> header(22 + images * 16);
auto *incondir = &header[0];
*reinterpret_cast<uint16_t *>(&incondir[0]) = 0; // reserved
*reinterpret_cast<uint16_t *>(&incondir[2]) = 1; // icon
*reinterpret_cast<uint16_t *>(&incondir[4]) = uint16_t(images);
std::vector<char> allData;
for (auto i = 0; i < images; ++i)
if (images > 255)
{
auto *inputAnyPath = argv[i + 2];
std::ifstream inputAny(inputAnyPath, std::ios::binary);
std::vector<char> data;
while (true)
{
char ch;
inputAny.read(&ch, 1);
if (inputAny.eof())
{
break;
}
if (!inputAny)
{
return 3;
}
data.push_back(ch);
}
if (*reinterpret_cast<uint64_t *>(&data[0]) != UINT64_C(0x0A1A0A0D474E5089)) // png magic
{
return 5;
}
auto width = uint8_t(data[19]);
auto height = uint8_t(data[23]);
auto *incondirentry = &header[6 + i * 16];
*reinterpret_cast<uint8_t *>(&incondirentry[0]) = width;
*reinterpret_cast<uint8_t *>(&incondirentry[1]) = height;
*reinterpret_cast<uint8_t *>(&incondirentry[2]) = 0; // no color palette
*reinterpret_cast<uint8_t *>(&incondirentry[3]) = 0; // reserved
*reinterpret_cast<uint16_t *>(&incondirentry[4]) = 1; // 1 color plane
*reinterpret_cast<uint16_t *>(&incondirentry[6]) = 32; // 32 bits per pixel
*reinterpret_cast<uint32_t *>(&incondirentry[8]) = uint32_t(data.size()); // data size
*reinterpret_cast<uint32_t *>(&incondirentry[12]) = uint32_t(header.size() + allData.size()); // data offset
allData.insert(allData.end(), data.begin(), data.end());
std::cerr << "too many images specified" << std::endl;
exit(1);
}
outputIco.write(&header[0], header.size());
outputIco.write(&allData[0], allData.size());
if (!outputIco)
std::string outputPath = argv[1];
std::ofstream output(outputPath, std::ios::binary);
auto outputFailure = [&outputPath](std::string action) {
std::cerr << "failed to " << action << " " << outputPath << ": " << strerror(errno) << std::endl;
exit(1);
};
if (!output)
{
return 4;
outputFailure("open");
}
std::vector<char> header(6 + images * 16, 0);
auto writeHeader = [&header, &output, &outputFailure]() {
output.seekp(0, std::ios_base::beg);
output.write(&header[0], header.size());
if (!output)
{
outputFailure("write");
}
};
writeHeader(); // make space for header
auto *headerU8 = reinterpret_cast<uint8_t *>(&header[0]);
headerU8[2] = 1;
headerU8[4] = images;
for (auto image = 0; image < images; ++image)
{
std::string inputPath = argv[2 + image];
std::ifstream input(inputPath, std::ios::binary);
auto inputFailure = [&inputPath](std::string action) {
std::cerr << "failed to " << action << " " << inputPath << ": " << strerror(errno) << std::endl;
exit(1);
};
auto imageFailure = [&inputPath](std::string failure) {
std::cerr << "failed to process " << inputPath << ": " << failure << std::endl;
exit(1);
};
if (!input)
{
inputFailure("open");
}
std::vector<char> buf;
input.seekg(0, std::ios_base::end);
buf.resize(input.tellg());
input.seekg(0, std::ios_base::beg);
input.read(&buf[0], buf.size());
if (!input)
{
inputFailure("read");
}
auto *bufU8 = reinterpret_cast<uint8_t *>(&buf[0]);
if (buf.size() < 0x21 ||
readU32BE(&bufU8[0]) != UINT32_C(0x89504E47) ||
readU32BE(&bufU8[4]) != UINT32_C(0x0D0A1A0A) ||
bufU8[0x18] != 8 ||
bufU8[0x19] != 6)
{
imageFailure("not a 32bpp RGBA PNG");
}
auto writeOffset = output.tellp();
output.write(&buf[0], buf.size());
if (!output)
{
outputFailure("write");
}
auto width = readU32BE(&bufU8[0x10]);
auto height = readU32BE(&bufU8[0x14]);
if (width == 256)
{
width = 0;
}
if (width > 255)
{
imageFailure("width exceeds U8 limit");
}
if (height == 256)
{
height = 0;
}
if (height > 255)
{
imageFailure("height exceeds U8 limit");
}
auto *entryU8 = headerU8 + 6 + image * 16;
entryU8[0] = width;
entryU8[1] = height;
entryU8[4] = 1;
entryU8[6] = 32;
if (buf.size() > UINT32_MAX)
{
imageFailure("data size exceeds U32 limit");
}
writeU32LE(&entryU8[8], uint32_t(buf.size()));
if (writeOffset > UINT32_MAX)
{
std::cerr << "output data size exceeds U32 limit" << std::endl;
exit(1);
}
writeU32LE(&entryU8[12], uint32_t(writeOffset));
}
writeHeader(); // actually write it out
return 0;
}

Binary file not shown.

View File

@ -32,46 +32,65 @@ else
endif
if host_platform == 'windows'
make_ico = executable('makeico', sources: 'MakeIco.cpp', native: true)
generated_win_icos = {}
win_icos = {
'icon_exe': [ 'icon_exe', 'icon_exe_48', 'icon_exe_32', 'icon_exe_16' ],
'icon_cps': [ 'icon_cps', 'icon_cps_48', 'icon_cps_32', 'icon_cps_16' ],
}
foreach key, icons : win_icos
command = [
make_ico,
'@OUTPUT@',
]
foreach ikey : icons
command += [ rendered_icons[ikey] ]
windows_icons = get_option('windows_icons')
windows_utf8cp = get_option('windows_utf8cp')
rc_conf_depends = []
rc_conf_depend_files = [
'resource.h',
]
icon_exe_ico_path = ''
icon_cps_ico_path = ''
winutf8_xml_path = ''
if windows_icons
make_ico = executable('makeico', sources: 'MakeIco.cpp', native: true)
generated_win_icos = {}
win_icos = {
'icon_exe': [ 'icon_exe', 'icon_exe_48', 'icon_exe_32', 'icon_exe_16' ],
'icon_cps': [ 'icon_cps', 'icon_cps_48', 'icon_cps_32', 'icon_cps_16' ],
}
foreach key, icons : win_icos
command = [
make_ico,
'@OUTPUT@',
]
foreach ikey : icons
command += [ rendered_icons[ikey] ]
endforeach
generated_win_icos += { key: custom_target(
key + '-ico',
output: key + '.ico',
command: command,
) }
endforeach
generated_win_icos += { key: custom_target(
key + '-ico',
output: key + '.ico',
command: command,
) }
endforeach
rc_conf_depends += [
generated_win_icos['icon_exe'],
generated_win_icos['icon_cps'],
]
icon_exe_ico_path = join_paths(meson.current_build_dir(), 'icon_exe.ico')
icon_cps_ico_path = join_paths(meson.current_build_dir(), 'icon_cps.ico')
endif
if windows_utf8cp
rc_conf_depend_files += [
'winutf8.xml',
]
winutf8_xml_path = join_paths(meson.current_source_dir(), 'winutf8.xml')
endif
rc_conf_data = configuration_data()
rc_conf_data.merge_from(conf_data)
rc_conf_data.set('HAVE_ICONS', windows_icons ? 1 : 0)
rc_conf_data.set('HAVE_UTF8CP', windows_utf8cp ? 1 : 0)
rc_conf_data.set('RESOUCE_H', join_paths(meson.current_source_dir(), 'resource.h'))
rc_conf_data.set('WINUTF8_XML', join_paths(meson.current_source_dir(), 'winutf8.xml'))
rc_conf_data.set('ICON_EXE_ICO', join_paths(meson.current_build_dir(), 'icon_exe.ico'))
rc_conf_data.set('ICON_CPS_ICO', join_paths(meson.current_build_dir(), 'icon_cps.ico'))
rc_conf_data.set('WINUTF8_XML', winutf8_xml_path)
rc_conf_data.set('ICON_EXE_ICO', icon_exe_ico_path)
rc_conf_data.set('ICON_CPS_ICO', icon_cps_ico_path)
powder_files += windows_mod.compile_resources(
configure_file(
input: 'powder-res.template.rc',
output: 'powder-res.rc',
configuration: rc_conf_data,
),
depends: [
generated_win_icos['icon_exe'],
generated_win_icos['icon_cps'],
],
depend_files: [
'resource.h',
'winutf8.xml',
],
depends: rc_conf_depends,
depend_files: rc_conf_depend_files,
)
elif host_platform == 'darwin'
configure_file(

View File

@ -5,9 +5,15 @@
#include <winver.h>
#include <ntdef.h>
#define HAVE_ICONS @HAVE_ICONS@
#define HAVE_UTF8CP @HAVE_UTF8CP@
#if HAVE_ICONS
IDI_ICON ICON DISCARDABLE "@ICON_EXE_ICO@"
IDI_DOC_ICON ICON DISCARDABLE "@ICON_CPS_ICO@"
#endif
#if HAVE_UTF8CP
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "@WINUTF8_XML@"
#endif
VS_VERSION_INFO VERSIONINFO
FILEVERSION @DISPLAY_VERSION_MAJOR@,@DISPLAY_VERSION_MINOR@,0,@BUILD_NUM@

View File

@ -18,6 +18,7 @@ constexpr bool INSTALL_CHECK = @INSTALL_CHECK@;
constexpr bool IGNORE_UPDATES = @IGNORE_UPDATES@;
constexpr bool ENFORCE_HTTPS = @ENFORCE_HTTPS@;
constexpr bool SECURE_CIPHERS_ONLY = @SECURE_CIPHERS_ONLY@;
constexpr bool PLATFORM_CLIPBOARD = @PLATFORM_CLIPBOARD@;
constexpr bool USE_SYSTEM_CERT_PROVIDER = @USE_SYSTEM_CERT_PROVIDER@;
constexpr bool FFTW_PLAN_MEASURE = @FFTW_PLAN_MEASURE@;
constexpr bool ALLOW_QUIT = @ALLOW_QUIT@;

View File

@ -10,12 +10,19 @@
#include "Format.h"
#include "graphics/Graphics.h"
ByteString format::UnixtimeToDate(time_t unixtime, ByteString dateFormat)
ByteString format::UnixtimeToDate(time_t unixtime, ByteString dateFormat, bool local)
{
struct tm * timeData;
char buffer[128];
timeData = localtime(&unixtime);
if (local)
{
timeData = localtime(&unixtime);
}
else
{
timeData = gmtime(&unixtime);
}
strftime(buffer, 128, dateFormat.c_str(), timeData);
return ByteString(buffer);

View File

@ -11,7 +11,7 @@ namespace format
{
ByteString URLEncode(ByteString value);
ByteString URLDecode(ByteString value);
ByteString UnixtimeToDate(time_t unixtime, ByteString dateFomat = ByteString("%d %b %Y"));
ByteString UnixtimeToDate(time_t unixtime, ByteString dateFomat = ByteString("%d %b %Y"), bool local = true);
ByteString UnixtimeToDateMini(time_t unixtime);
String CleanString(String dirtyString, bool ascii, bool color, bool newlines, bool numeric = false);
std::vector<char> PixelsToPPM(PlaneAdapter<std::vector<pixel>> const &);

View File

@ -51,6 +51,11 @@ inline int isign(float i)
return 0;
}
inline int iabs(int i)
{
return i * isign(i);
}
inline unsigned clamp_flt(float f, float min, float max)
{
if (f<min)

View File

@ -22,6 +22,7 @@
#include "gui/dialogues/ConfirmPrompt.h"
#include "gui/dialogues/ErrorMessage.h"
#include "gui/interface/Engine.h"
#include "gui/interface/TextWrapper.h"
#include "Config.h"
#include "SimulationConfig.h"
#include <optional>
@ -29,6 +30,8 @@
#include <iostream>
#include <csignal>
#include <SDL.h>
#include <exception>
#include <cstdlib>
void LoadWindowPosition()
{
@ -97,38 +100,70 @@ void TickClient()
Client::Ref().Tick();
}
void BlueScreen(String detailMessage)
static void BlueScreen(String detailMessage, std::optional<std::vector<String>> stackTrace)
{
auto &engine = ui::Engine::Ref();
engine.g->BlendFilledRect(engine.g->Size().OriginRect(), 0x1172A9_rgb .WithAlpha(0xD2));
String errorTitle = "ERROR";
String errorDetails = "Details: " + detailMessage;
String errorHelp = String("An unrecoverable fault has occurred, please report the error by visiting the website below\n") + SCHEME + SERVER;
auto versionInfo = ByteString::Build("Version: ", VersionInfo(), "\nTag: ", VCS_TAG).FromUtf8();
auto crashPrevLogPath = ByteString("crash.prev.log");
auto crashLogPath = ByteString("crash.log");
Platform::RenameFile(crashLogPath, crashPrevLogPath, true);
// We use the width of errorHelp to center, but heights of the individual texts for vertical spacing
auto pos = engine.g->Size() / 2 - Vec2(Graphics::TextSize(errorHelp).X / 2, 100);
engine.g->BlendText(pos, errorTitle, 0xFFFFFF_rgb .WithAlpha(0xFF));
pos.Y += 4 + Graphics::TextSize(errorTitle).Y;
engine.g->BlendText(pos, errorDetails, 0xFFFFFF_rgb .WithAlpha(0xFF));
pos.Y += 4 + Graphics::TextSize(errorDetails).Y;
engine.g->BlendText(pos, errorHelp, 0xFFFFFF_rgb .WithAlpha(0xFF));
pos.Y += 4 + Graphics::TextSize(errorHelp).Y;
engine.g->BlendText(pos, versionInfo, 0xFFFFFF_rgb .WithAlpha(0xFF));
StringBuilder crashInfo;
crashInfo << "ERROR - Details: " << detailMessage << "\n";
crashInfo << "An unrecoverable fault has occurred, please report it by visiting the website below\n\n " << SCHEME << SERVER << "\n\n";
crashInfo << "An attempt will be made to save all of this information to " << crashLogPath.FromUtf8() << " in your data folder.\n";
crashInfo << "Please attach this file to your report.\n\n";
crashInfo << "Version: " << VersionInfo().FromUtf8() << "\n";
crashInfo << "Tag: " << VCS_TAG << "\n";
crashInfo << "Date: " << format::UnixtimeToDate(time(NULL), "%Y-%m-%dT%H:%M:%SZ", false).FromUtf8() << "\n";
if (stackTrace)
{
crashInfo << "Stack trace:\n";
for (auto &item : *stackTrace)
{
crashInfo << " - " << item << "\n";
}
}
else
{
crashInfo << "Stack trace not available\n";
}
String errorText = crashInfo.Build();
constexpr auto width = 440;
ui::TextWrapper tw;
tw.Update(errorText, true, width);
engine.g->BlendText(ui::Point((engine.g->Size().X - width) / 2, 80), tw.WrappedText(), 0xFFFFFF_rgb .WithAlpha(0xFF));
auto crashLogData = errorText.ToUtf8();
std::cerr << crashLogData << std::endl;
Platform::WriteFile(std::vector<char>(crashLogData.begin(), crashLogData.end()), crashLogPath);
//Death loop
SDL_Event event;
while(true)
auto running = true;
while (running)
{
while (SDL_PollEvent(&event))
if(event.type == SDL_QUIT)
exit(-1); // Don't use Platform::Exit, we're practically zombies at this point anyway.
{
if (event.type == SDL_QUIT)
{
running = false;
}
}
blit(engine.g->Data());
}
// Don't use Platform::Exit, we're practically zombies at this point anyway.
#if defined(__MINGW32__) || defined(__APPLE__) || defined(__EMSCRIPTEN__)
// Come on...
exit(-1);
#else
quick_exit(-1);
#endif
}
struct
static struct
{
int sig;
const char *message;
@ -140,7 +175,7 @@ struct
{ 0, nullptr },
};
void SigHandler(int signal)
static void SigHandler(int signal)
{
const char *message = "Unknown signal";
for (auto *msg = signalMessages; msg->message; ++msg)
@ -151,7 +186,29 @@ void SigHandler(int signal)
break;
}
}
BlueScreen(ByteString(message).FromUtf8());
BlueScreen(ByteString(message).FromUtf8(), Platform::StackTrace());
}
static void TerminateHandler()
{
ByteString err = "std::terminate called without a current exception";
auto eptr = std::current_exception();
try
{
if (eptr)
{
std::rethrow_exception(eptr);
}
}
catch (const std::exception &e)
{
err = "unhandled exception: " + ByteString(e.what());
}
catch (...)
{
err = "unhandled exception not derived from std::exception, cannot determine reason";
}
BlueScreen(err.FromUtf8(), Platform::StackTrace());
}
constexpr int SCALE_MAXIMUM = 10;
@ -388,19 +445,20 @@ int Main(int argc, char *argv[])
engine.Begin();
engine.SetFastQuit(prefs.Get("FastQuit", true));
engine.TouchUI = prefs.Get("TouchUI", DEFAULT_TOUCH_UI);
engine.windowFrameOps = windowFrameOps;
SDLOpen();
if (Client::Ref().IsFirstRun() && FORCE_WINDOW_FRAME_OPS == forceWindowFrameOpsNone)
{
auto guessed = GuessBestScale();
if (windowFrameOps.scale != guessed)
if (engine.windowFrameOps.scale != guessed)
{
windowFrameOps.scale = guessed;
engine.windowFrameOps.scale = guessed;
prefs.Set("Scale", windowFrameOps.scale);
showLargeScreenDialog = true;
}
}
engine.windowFrameOps = windowFrameOps;
SDLOpen();
bool enableBluescreen = USE_BLUESCREEN && !true_arg(arguments["disable-bluescreen"]);
if (enableBluescreen)
@ -410,6 +468,7 @@ int Main(int argc, char *argv[])
{
signal(msg->sig, SigHandler);
}
std::set_terminate(TerminateHandler);
}
if constexpr (X86)
@ -417,114 +476,97 @@ int Main(int argc, char *argv[])
X86KillDenormals();
}
auto wrapWithBluescreen = [&]() {
explicitSingletons->simulationData = std::make_unique<SimulationData>();
explicitSingletons->gameController = std::make_unique<GameController>();
auto *gameController = explicitSingletons->gameController.get();
engine.ShowWindow(gameController->GetView());
explicitSingletons->simulationData = std::make_unique<SimulationData>();
explicitSingletons->gameController = std::make_unique<GameController>();
auto *gameController = explicitSingletons->gameController.get();
engine.ShowWindow(gameController->GetView());
auto openArg = arguments["open"];
if (openArg.has_value())
auto openArg = arguments["open"];
if (openArg.has_value())
{
if constexpr (DEBUG)
{
if constexpr (DEBUG)
{
std::cout << "Loading " << openArg.value() << std::endl;
}
if (Platform::FileExists(openArg.value()))
{
try
{
std::vector<char> gameSaveData;
if (!Platform::ReadFile(gameSaveData, openArg.value()))
{
new ErrorMessage("Error", "Could not read file");
}
else
{
auto newFile = std::make_unique<SaveFile>(openArg.value());
auto newSave = std::make_unique<GameSave>(std::move(gameSaveData));
newFile->SetGameSave(std::move(newSave));
gameController->LoadSaveFile(std::move(newFile));
}
}
catch (std::exception & e)
{
new ErrorMessage("Error", "Could not open save file:\n" + ByteString(e.what()).FromUtf8()) ;
}
}
else
{
new ErrorMessage("Error", "Could not open file");
}
std::cout << "Loading " << openArg.value() << std::endl;
}
auto ptsaveArg = arguments["ptsave"];
if (ptsaveArg.has_value())
if (Platform::FileExists(openArg.value()))
{
engine.g->Clear();
engine.g->DrawRect(RectSized(engine.g->Size() / 2 - Vec2(100, 25), Vec2(200, 50)), 0xB4B4B4_rgb);
String loadingText = "Loading save...";
engine.g->BlendText(engine.g->Size() / 2 - Vec2((Graphics::TextSize(loadingText).X - 1) / 2, 5), loadingText, style::Colour::InformationTitle);
blit(engine.g->Data());
try
{
ByteString saveIdPart;
if (ByteString::Split split = ptsaveArg.value().SplitBy(':'))
std::vector<char> gameSaveData;
if (!Platform::ReadFile(gameSaveData, openArg.value()))
{
if (split.Before() != "ptsave")
throw std::runtime_error("Not a ptsave link");
saveIdPart = split.After().SplitBy('#').Before();
new ErrorMessage("Error", "Could not read file");
}
else
throw std::runtime_error("Invalid save link");
{
auto newFile = std::make_unique<SaveFile>(openArg.value());
auto newSave = std::make_unique<GameSave>(std::move(gameSaveData));
newFile->SetGameSave(std::move(newSave));
gameController->LoadSaveFile(std::move(newFile));
}
if (!saveIdPart.size())
throw std::runtime_error("No Save ID");
if constexpr (DEBUG)
{
std::cout << "Got Ptsave: id: " << saveIdPart << std::endl;
}
ByteString saveHistoryPart = "0";
if (auto split = saveIdPart.SplitBy('@'))
{
saveHistoryPart = split.After();
saveIdPart = split.Before();
}
int saveId = saveIdPart.ToNumber<int>();
int saveHistory = saveHistoryPart.ToNumber<int>();
gameController->OpenSavePreview(saveId, saveHistory, savePreviewUrl);
}
catch (std::exception & e)
{
new ErrorMessage("Error", ByteString(e.what()).FromUtf8());
Platform::MarkPresentable();
new ErrorMessage("Error", "Could not open save file:\n" + ByteString(e.what()).FromUtf8()) ;
}
}
else
{
Platform::MarkPresentable();
new ErrorMessage("Error", "Could not open file");
}
}
MainLoop();
};
if (enableBluescreen)
auto ptsaveArg = arguments["ptsave"];
if (ptsaveArg.has_value())
{
engine.g->Clear();
engine.g->DrawRect(RectSized(engine.g->Size() / 2 - Vec2(100, 25), Vec2(200, 50)), 0xB4B4B4_rgb);
String loadingText = "Loading save...";
engine.g->BlendText(engine.g->Size() / 2 - Vec2((Graphics::TextSize(loadingText).X - 1) / 2, 5), loadingText, style::Colour::InformationTitle);
blit(engine.g->Data());
try
{
wrapWithBluescreen();
ByteString saveIdPart;
if (ByteString::Split split = ptsaveArg.value().SplitBy(':'))
{
if (split.Before() != "ptsave")
throw std::runtime_error("Not a ptsave link");
saveIdPart = split.After().SplitBy('#').Before();
}
else
throw std::runtime_error("Invalid save link");
if (!saveIdPart.size())
throw std::runtime_error("No Save ID");
if constexpr (DEBUG)
{
std::cout << "Got Ptsave: id: " << saveIdPart << std::endl;
}
ByteString saveHistoryPart = "0";
if (auto split = saveIdPart.SplitBy('@'))
{
saveHistoryPart = split.After();
saveIdPart = split.Before();
}
int saveId = saveIdPart.ToNumber<int>();
int saveHistory = saveHistoryPart.ToNumber<int>();
gameController->OpenSavePreview(saveId, saveHistory, savePreviewUrl);
}
catch (const std::exception &e)
catch (std::exception & e)
{
BlueScreen(ByteString(e.what()).FromUtf8());
new ErrorMessage("Error", ByteString(e.what()).FromUtf8());
Platform::MarkPresentable();
}
}
else
{
wrapWithBluescreen();
Platform::MarkPresentable();
}
MainLoop();
Platform::Exit(0);
return 0;
}

View File

@ -75,7 +75,7 @@ int main(int argc, char * argv[])
}
else
{
std::cerr << "path to font.cpp not supplied" << std::endl;
std::cerr << "path to font.bz2 not supplied" << std::endl;
Platform::Exit(1);
}

View File

@ -22,6 +22,8 @@ int main(int argc, char *argv[])
auto inputFilename = ByteString(argv[1]);
auto outputFilename = ByteString(argv[2]) + ".png";
auto simulationData = std::make_unique<SimulationData>();
std::vector<char> fileData;
if (!Platform::ReadFile(fileData, inputFilename))
{
@ -40,7 +42,6 @@ int main(int argc, char *argv[])
throw e;
}
auto simulationData = std::make_unique<SimulationData>();
Simulation * sim = new Simulation();
Renderer * ren = new Renderer(sim);
@ -63,6 +64,7 @@ int main(int argc, char *argv[])
}
else
{
ren->clearScreen();
int w = Graphics::TextSize("Save file invalid").X + 15, x = (XRES-w)/2, y = (YRES-24)/2;
ren->DrawRect(RectSized(Vec2{ x, y }, Vec2{ w, 24 }), 0xC0C0C0_rgb);
ren->BlendText({ x+8, y+8 }, "Save file invalid", 0xC0C0F0_rgb .WithAlpha(255));

View File

@ -349,7 +349,7 @@ static void EventProcess(const SDL_Event &event)
mousey = event.button.y;
}
mouseButton = event.button.button;
engine.onMouseClick(mousex, mousey, mouseButton);
engine.onMouseDown(mousex, mousey, mouseButton);
mouseDown = true;
if constexpr (!DEBUG)
@ -365,7 +365,7 @@ static void EventProcess(const SDL_Event &event)
mousey = event.button.y;
}
mouseButton = event.button.button;
engine.onMouseUnclick(mousex, mousey, mouseButton);
engine.onMouseUp(mousex, mousey, mouseButton);
mouseDown = false;
if constexpr (!DEBUG)

View File

@ -42,10 +42,13 @@ constexpr float AIR_PLOSS = 0.9999f;
constexpr int NGOL = 24;
constexpr int CIRCLE_BRUSH = 0;
constexpr int SQUARE_BRUSH = 1;
constexpr int TRI_BRUSH = 2;
constexpr int BRUSH_NUM = 3;
enum DefaultBrushes
{
BRUSH_CIRCLE,
BRUSH_SQUARE,
BRUSH_TRIANGLE,
NUM_DEFAULTBRUSHES,
};
//Photon constants
constexpr int SURF_RANGE = 10;

View File

@ -3,7 +3,6 @@
#include "client/http/StartupRequest.h"
#include "ClientListener.h"
#include "Format.h"
#include "MD5.h"
#include "client/GameSave.h"
#include "client/SaveFile.h"
#include "client/SaveInfo.h"
@ -11,6 +10,7 @@
#include "common/platform/Platform.h"
#include "common/String.h"
#include "graphics/Graphics.h"
#include "gui/dialogues/ErrorMessage.h"
#include "prefs/Prefs.h"
#include "lua/CommandInterface.h"
#include "Config.h"
@ -117,7 +117,7 @@ void Client::Tick()
{
if (versionCheckRequest->StatusCode() == 618)
{
AddServerNotification({ "Failed to load SSL certificates", ByteString(SCHEME) + "powdertoy.co.uk/FAQ.html" });
AddServerNotification({ "Failed to load SSL certificates", ByteString::Build(SCHEME, SERVER, "/FAQ.html") });
}
try
{
@ -130,7 +130,7 @@ void Client::Tick()
{
updateInfo = info.updateInfo;
applyUpdateInfo = true;
messageOfTheDay = info.messageOfTheDay;
SetMessageOfTheDay(info.messageOfTheDay);
}
for (auto &notification : info.notifications)
{
@ -141,7 +141,7 @@ void Client::Tick()
{
if (!usingAltUpdateServer)
{
messageOfTheDay = ByteString::Build("Error while fetching MotD: ", ex.what()).FromUtf8();
SetMessageOfTheDay(ByteString::Build("Error while fetching MotD: ", ex.what()).FromUtf8());
}
}
versionCheckRequest.reset();
@ -153,7 +153,7 @@ void Client::Tick()
auto info = alternateVersionCheckRequest->Finish();
updateInfo = info.updateInfo;
applyUpdateInfo = true;
messageOfTheDay = info.messageOfTheDay;
SetMessageOfTheDay(info.messageOfTheDay);
for (auto &notification : info.notifications)
{
AddServerNotification(notification);
@ -161,7 +161,7 @@ void Client::Tick()
}
catch (const http::RequestError &ex)
{
messageOfTheDay = ByteString::Build("Error while checking for updates: ", ex.what()).FromUtf8();
SetMessageOfTheDay(ByteString::Build("Error while checking for updates: ", ex.what()).FromUtf8());
}
alternateVersionCheckRequest.reset();
}
@ -287,6 +287,27 @@ void Client::DeleteStamp(ByteString stampID)
}
}
void Client::RenameStamp(ByteString stampID, ByteString newName)
{
auto oldPath = ByteString::Build(STAMPS_DIR, PATH_SEP_CHAR, stampID, ".stm");
auto newPath = ByteString::Build(STAMPS_DIR, PATH_SEP_CHAR, newName, ".stm");
if (Platform::FileExists(newPath))
{
new ErrorMessage("Error renaming stamp", "A stamp with this name already exists.");
return;
}
if (!Platform::RenameFile(oldPath, newPath, false))
{
new ErrorMessage("Error renaming stamp", "Could not rename the stamp.");
return;
}
std::replace(stampIDs.begin(), stampIDs.end(), stampID, newName);
WriteStamps();
}
ByteString Client::AddStamp(std::unique_ptr<GameSave> saveData)
{
auto now = (uint64_t)time(NULL);
@ -356,7 +377,6 @@ void Client::RescanStamps()
newStampIDs.push_back(stampID);
}
}
auto oldCount = newStampIDs.size();
auto stampIDsSet = std::set<ByteString>(stampIDs.begin(), stampIDs.end());
for (auto &stampID : stampFilesSet)
{
@ -368,8 +388,6 @@ void Client::RescanStamps()
}
if (changed)
{
// Move newly discovered stamps to front.
std::rotate(newStampIDs.begin(), newStampIDs.begin() + oldCount, newStampIDs.end());
stampIDs = newStampIDs;
WriteStamps();
}
@ -423,7 +441,6 @@ std::unique_ptr<SaveFile> Client::LoadSaveFile(ByteString filename)
{
file->SetLoadingError(err.FromUtf8());
}
commandInterface->SetLastError(err.FromUtf8());
}
return file;
}

View File

@ -91,6 +91,7 @@ public:
std::unique_ptr<SaveFile> GetStamp(ByteString stampID);
void DeleteStamp(ByteString stampID);
void RenameStamp(ByteString stampID, ByteString newName);
ByteString AddStamp(std::unique_ptr<GameSave> saveData);
void RescanStamps();
const std::vector<ByteString> &GetStamps() const;

View File

@ -51,9 +51,18 @@ GameSave::GameSave(const std::vector<char> &data, bool newWantAuthors)
void GameSave::MapPalette()
{
int partMap[PT_NUM];
bool ignoreMissingErrors[PT_NUM];
for(int i = 0; i < PT_NUM; i++)
{
partMap[i] = i;
ignoreMissingErrors[i] = false;
}
if (version <= Version(98, 2))
{
ignoreMissingErrors[PT_ICEI] = true;
ignoreMissingErrors[PT_SNOW] = true;
ignoreMissingErrors[PT_RSST] = true;
ignoreMissingErrors[PT_RSSS] = true;
}
auto &sd = SimulationData::CRef();
@ -90,12 +99,14 @@ void GameSave::MapPalette()
}
}
}
auto paletteLookup = [this, &partMap](int type) {
auto paletteLookup = [this, &partMap](int type, bool ignoreMissingErrors) {
if (type > 0 && type < PT_NUM)
{
auto carriedType = partMap[type];
if (!carriedType) // type is not 0 so this shouldn't be 0 either
{
if (ignoreMissingErrors)
return type;
missingElements.ids.insert(type);
}
type = carriedType;
@ -113,7 +124,7 @@ void GameSave::MapPalette()
{
continue;
}
tempPart.type = paletteLookup(tempPart.type);
tempPart.type = paletteLookup(tempPart.type, false);
for (auto index : possiblyCarriesType)
{
if (elements[tempPart.type].CarriesTypeIn & (1U << index))
@ -121,7 +132,7 @@ void GameSave::MapPalette()
auto *prop = reinterpret_cast<int *>(reinterpret_cast<char *>(&tempPart) + properties[index].Offset);
auto carriedType = *prop & int(pmapmask);
auto extra = *prop >> pmapbits;
carriedType = paletteLookup(carriedType);
carriedType = paletteLookup(carriedType, ignoreMissingErrors[tempPart.type]);
*prop = PMAP(extra, carriedType);
}
}
@ -1853,26 +1864,26 @@ void GameSave::readPSv(const std::vector<char> &dataVec)
return;
auto signCount = data[p++];
for (auto i=0; i<signCount; i++)
for (auto i = 0; i < signCount; i++)
{
if (p+6 > dataLength)
throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
{
auto x = data[p++];
int x = data[p++];
x |= ((unsigned)data[p++])<<8;
tempSign.x = x+partP.X;
}
{
auto y = data[p++];
int y = data[p++];
y |= ((unsigned)data[p++])<<8;
tempSign.y = y+partP.Y;
}
{
auto ju = data[p++];
int ju = data[p++];
tempSign.ju = (sign::Justification)ju;
}
{
auto l = data[p++];
int l = data[p++];
if (p+l > dataLength)
throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__));
if(l>254)
@ -2336,6 +2347,14 @@ std::pair<bool, std::vector<char>> GameSave::serialiseOPS() const
{
RESTRICTVERSION(97, 0);
}
if (particles[i].type == PT_RSST || particles[i].type == PT_RSSS)
{
RESTRICTVERSION(98, 0);
}
if (particles[i].type == PT_ETRD && (particles[i].tmp || particles[i].tmp2))
{
RESTRICTVERSION(98, 0);
}
//Get the pmap entry for the next particle in the same position
i = partsPosLink[i];

View File

@ -1,231 +0,0 @@
// based on public-domain code from Colin Plumb (1993)
#include "MD5.h"
#include <cstring>
static unsigned getu32(const unsigned char *addr)
{
return (((((unsigned long)addr[3] << 8) | addr[2]) << 8) | addr[1]) << 8 | addr[0];
}
static void putu32(unsigned data, unsigned char *addr)
{
addr[0] = (unsigned char)data;
addr[1] = (unsigned char)(data >> 8);
addr[2] = (unsigned char)(data >> 16);
addr[3] = (unsigned char)(data >> 24);
}
void md5_init(struct md5_context *ctx)
{
ctx->buf[0] = 0x67452301;
ctx->buf[1] = 0xefcdab89;
ctx->buf[2] = 0x98badcfe;
ctx->buf[3] = 0x10325476;
ctx->bits[0] = 0;
ctx->bits[1] = 0;
}
void md5_update(struct md5_context *ctx, unsigned char const *buf, unsigned len)
{
unsigned t;
// update bit count
t = ctx->bits[0];
if ((ctx->bits[0] = (t + ((unsigned)len << 3)) & 0xffffffff) < t)
ctx->bits[1]++; // carry
ctx->bits[1] += len >> 29;
t = (t >> 3) & 0x3f;
// use leading data to top up the buffer
if (t)
{
unsigned char *p = ctx->in + t;
t = 64-t;
if (len < t)
{
memcpy(p, buf, len);
return;
}
memcpy(p, buf, t);
md5_transform(ctx->buf, ctx->in);
buf += t;
len -= t;
}
// following 64-byte chunks
while (len >= 64)
{
memcpy(ctx->in, buf, 64);
md5_transform(ctx->buf, ctx->in);
buf += 64;
len -= 64;
}
// save rest of bytes for later
memcpy(ctx->in, buf, len);
}
void md5_final(unsigned char digest[16], struct md5_context *ctx)
{
unsigned count;
unsigned char *p;
// #bytes mod64
count = (ctx->bits[0] >> 3) & 0x3F;
// first char of padding = 0x80
p = ctx->in + count;
*p++ = 0x80;
// calculate # of bytes to pad
count = 64 - 1 - count;
// Pad out to 56 mod 64
if (count < 8)
{
// we need to finish a whole block before padding
memset(p, 0, count);
md5_transform(ctx->buf, ctx->in);
memset(ctx->in, 0, 56);
}
else
{
// just pad to 56 bytes
memset(p, 0, count-8);
}
// append length & final transform
putu32(ctx->bits[0], ctx->in + 56);
putu32(ctx->bits[1], ctx->in + 60);
md5_transform(ctx->buf, ctx->in);
putu32(ctx->buf[0], digest);
putu32(ctx->buf[1], digest + 4);
putu32(ctx->buf[2], digest + 8);
putu32(ctx->buf[3], digest + 12);
memset(&ctx, 0, sizeof(ctx));
}
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
#define MD5STEP(f, w, x, y, z, data, s) \
( w += f(x, y, z) + data, w &= 0xffffffff, w = w<<s | w>>(32-s), w += x )
void md5_transform(unsigned buf[4], const unsigned char inraw[64])
{
unsigned a, b, c, d;
unsigned in[16];
int i;
for (i = 0; i < 16; ++i)
in[i] = getu32 (inraw + 4 * i);
a = buf[0];
b = buf[1];
c = buf[2];
d = buf[3];
MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7);
MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7);
MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7);
MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7);
MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5);
MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9);
MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5);
MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9);
MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5);
MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9);
MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5);
MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9);
MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4);
MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4);
MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4);
MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4);
MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6);
MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6);
MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6);
MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6);
MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
buf[0] += a;
buf[1] += b;
buf[2] += c;
buf[3] += d;
}
static char hexChars[] = "0123456789abcdef";
void md5_ascii(char *result, unsigned char const *buf, unsigned len)
{
struct md5_context md5;
unsigned char hash[16];
int i;
if (len==0)
len = strlen((char *)buf);
md5_init(&md5);
md5_update(&md5, buf, len);
md5_final(hash, &md5);
for (i=0; i<16; i++)
{
result[i*2] = hexChars[(hash[i]>>4)&0xF];
result[i*2+1] = hexChars[hash[i]&0x0F];
}
result[32] = 0;
}

View File

@ -1,15 +0,0 @@
#pragma once
struct md5_context
{
unsigned buf[4];
unsigned bits[2];
unsigned char in[64];
};
void md5_init(struct md5_context *context);
void md5_update(struct md5_context *context, unsigned char const *buf, unsigned len);
void md5_final(unsigned char digest[16], struct md5_context *context);
void md5_transform(unsigned buf[4], const unsigned char in[64]);
void md5_ascii(char *result, unsigned char const *buf, unsigned len);

View File

@ -14,4 +14,13 @@ namespace http
sortByVotes,
sortByDate,
};
enum Period
{
allSaves,
todaySaves,
weekSaves,
monthSaves,
yearSaves,
};
}

View File

@ -5,7 +5,7 @@
namespace http
{
LogoutRequest::LogoutRequest() :
APIRequest(ByteString::Build(SCHEME, SERVER, "/Logout.json?Key=" + Client::Ref().GetAuthUser().SessionKey), authRequire, false)
APIRequest(ByteString::Build(SCHEME, SERVER, "/Logout.json?Key=" + Client::Ref().GetAuthUser().SessionKey), authRequire, true)
{
}

View File

@ -1,3 +1,4 @@
#include <ctime>
#include "SearchSavesRequest.h"
#include "Config.h"
#include "client/Client.h"
@ -6,7 +7,7 @@
namespace http
{
static ByteString Url(int start, int count, ByteString query, Sort sort, Category category)
static ByteString Url(int start, int count, ByteString query, Period period, Sort sort, Category category)
{
ByteStringBuilder builder;
builder << SCHEME << SERVER << "/Browse.json?Start=" << start << "&Count=" << count;
@ -17,6 +18,38 @@ namespace http
}
query += str;
};
time_t currentTime = time(NULL);
if(period)
{
switch (period)
{
case todaySaves:
currentTime -= 60*60*24; // One day
break;
case weekSaves:
currentTime -= 60*60*24*7; // One week
break;
case monthSaves:
currentTime -= 60*60*24*31; // One month
break;
case yearSaves:
currentTime -= 60*60*24*365; // One year
break;
default:
break;
}
struct tm currentTimeData = *localtime(&currentTime);
ByteStringBuilder afterQuery;
afterQuery << "after:" << currentTimeData.tm_year+1900 << "-" <<
(currentTimeData.tm_mon < 9 ? "0" : "") << currentTimeData.tm_mon+1 << "-" <<
(currentTimeData.tm_mday < 10 ? "0" : "") << currentTimeData.tm_mday;
appendToQuery(afterQuery.Build());
}
switch (sort)
{
case sortByDate:
@ -48,7 +81,7 @@ namespace http
return builder.Build();
}
SearchSavesRequest::SearchSavesRequest(int start, int count, ByteString query, Sort sort, Category category) : APIRequest(Url(start, count, query, sort, category), authUse, false)
SearchSavesRequest::SearchSavesRequest(int start, int count, ByteString query, Period period, Sort sort, Category category) : APIRequest(Url(start, count, query, period, sort, category), authUse, false)
{
}

View File

@ -8,7 +8,7 @@ namespace http
class SearchSavesRequest : public APIRequest
{
public:
SearchSavesRequest(int start, int count, ByteString query, Sort sort, Category category);
SearchSavesRequest(int start, int count, ByteString query, Period period, Sort sort, Category category);
std::pair<int, std::vector<std::unique_ptr<SaveInfo>>> Finish();
};

View File

@ -52,6 +52,10 @@ namespace http
return;
}
auto &info = versions[key];
if (info.isNull())
{
return;
}
auto getOr = [&info](ByteString key, int defaultValue) -> int {
if (!info.isMember(key))
{
@ -59,7 +63,7 @@ namespace http
}
return info[key].asInt();
};
auto build = getOr(key == "Snapshot" ? "Snapshot" : "Build", -1);
auto build = getOr(key == "Snapshot" ? "Snapshot" : "Build", 0);
if (!updateAvailableFunc(build))
{
return;
@ -68,8 +72,8 @@ namespace http
channel,
ByteString::Build(SCHEME, alternate ? UPDATESERVER : SERVER, info["File"].asString()),
ByteString(info["Changelog"].asString()).FromUtf8(),
getOr("Major", -1),
getOr("Minor", -1),
getOr("Major", 0),
getOr("Minor", 0),
build,
};
};

View File

@ -296,13 +296,8 @@ namespace http
{
{
std::lock_guard lk(sharedStateMx);
for (auto &requestHandle : requestHandles)
{
if (requestHandle->statusCode)
{
requestHandlesToUnregister.push_back(requestHandle);
}
}
// Register new handles first. This always succeeds even if the handle is "failed early" so that
// a single MarkDone call could be issued on all handles further down in this block.
for (auto &requestHandle : requestHandlesToRegister)
{
// Must not be present
@ -311,6 +306,17 @@ namespace http
RegisterRequestHandle(requestHandle);
}
requestHandlesToRegister.clear();
// Then unregister done handles. As explained above, registering a new handle may also immediately mark
// it done and we won't be coming back here until Wait() returns, so this has to come second.
for (auto &requestHandle : requestHandles)
{
if (requestHandle->statusCode)
{
requestHandlesToUnregister.push_back(requestHandle);
}
}
// Actually unregister handles queued to be unregistered. They can be queued just above, or from another thread.
// Thus, it's ok for them to be in the queue multiple times, but it's not ok to try to unregister them multiple times.
for (auto &requestHandle : requestHandlesToUnregister)
{
auto eraseFrom = std::remove(requestHandles.begin(), requestHandles.end(), requestHandle);

View File

@ -12,6 +12,7 @@ else
if host_platform == 'windows'
use_system_cert_provider = true
client_files += files('WindowsCertProvider.cpp')
powder_deps += c_compiler.find_library('crypt32')
endif
if host_platform == 'android'
use_system_cert_provider = true

View File

@ -1,5 +1,4 @@
client_files = files(
'MD5.cpp',
'SaveFile.cpp',
'SaveInfo.cpp',
'ThumbnailRendererTask.cpp',

23
src/common/Defer.h Normal file
View File

@ -0,0 +1,23 @@
#pragma once
#include <functional>
class Defer
{
std::function<void ()> func;
public:
Defer(std::function<void ()> newFunc) : func(newFunc)
{
}
Defer(const Defer &other) = delete;
Defer &operator =(const Defer &other) = delete;
~Defer()
{
if (func)
{
func();
}
}
};

View File

@ -4,7 +4,6 @@
#include <limits>
#include <stdexcept>
#include "common/tpt-thread-local.h"
#include "String.h"
ByteString ConversionError::formatError(ByteString::value_type const *at, ByteString::value_type const *upto)
@ -376,7 +375,7 @@ struct LocaleImpl
static LocaleImpl *getLocaleImpl()
{
static THREAD_LOCAL(LocaleImpl, li);
thread_local LocaleImpl li;
return &li;
}

View File

@ -112,6 +112,14 @@ struct Vec2
);
}
Vec2<T> Min(Vec2<T> other) const
{
return Vec2<T>(
std::min(X, other.X),
std::min(Y, other.Y)
);
}
// Return a rectangle starting at origin, whose dimensions match this vector
template<typename S = T, typename = std::enable_if_t<std::is_integral_v<S>>>
constexpr inline Rect<T> OriginRect() const

View File

@ -1,5 +1,6 @@
#pragma once
#include <memory>
#include <optional>
#include "common/String.h"
class GameSave;
@ -10,5 +11,8 @@ namespace Clipboard
void SetClipboardData(std::unique_ptr<GameSave> data);
const GameSave *GetClipboardData();
void Init();
bool GetEnabled();
void SetEnabled(bool newEnabled);
void RecreateWindow();
std::optional<String> Explanation();
}

View File

@ -62,6 +62,11 @@ namespace Clipboard
}
return gdc;
}
std::optional<String> Explanation() final override
{
return std::nullopt;
}
};
std::unique_ptr<ClipboardImpl> CocoaClipboardFactory()

View File

@ -1,4 +1,5 @@
#include "Dynamic.h"
#include "Clipboard.h"
#include "client/GameSave.h"
#include "prefs/GlobalPrefs.h"
#include "PowderToySDL.h"
@ -23,7 +24,7 @@ namespace Clipboard
};
std::unique_ptr<GameSave> clipboardData;
std::unique_ptr<ClipboardImpl> clipboard;
static std::unique_ptr<ClipboardImpl> clipboard;
void InvokeClipboardSetClipboardData()
{
@ -90,8 +91,21 @@ namespace Clipboard
return clipboardData.get();
}
static bool enabled = false;
void Init()
{
enabled = GlobalPrefs::Ref().Get("NativeClipboard.Enabled", false);
}
bool GetEnabled()
{
return enabled;
}
void SetEnabled(bool newEnabled)
{
enabled = newEnabled;
RecreateWindow();
}
int currentSubsystem;
@ -104,7 +118,7 @@ namespace Clipboard
SDL_GetWindowWMInfo(sdl_window, &info);
clipboard.reset();
currentSubsystem = info.subsystem;
if (GlobalPrefs::Ref().Get<bool>("NativeClipboard.Enabled", true))
if (enabled)
{
for (auto *impl = clipboardImpls; impl->factory; ++impl)
{
@ -117,4 +131,9 @@ namespace Clipboard
}
InvokeClipboardSetClipboardData();
}
std::optional<String> Explanation()
{
return clipboard ? clipboard->Explanation() : std::nullopt;
}
}

View File

@ -3,6 +3,7 @@
#include <vector>
#include <variant>
#include <memory>
#include <optional>
class GameSave;
@ -35,6 +36,8 @@ namespace Clipboard
GetClipboardDataUnknown
>;
virtual GetClipboardDataResult GetClipboardData() = 0;
virtual std::optional<String> Explanation() = 0;
};
extern std::unique_ptr<GameSave> clipboardData;

View File

@ -18,6 +18,7 @@ namespace Clipboard
ByteString inCommand;
ByteString formatsCommand;
ByteString outCommand;
std::optional<String> explanation;
std::optional<int> defaultForSubsystem;
};
std::map<ByteString, Preset> builtInPresets = {
@ -25,12 +26,14 @@ namespace Clipboard
"xclip -selection clipboard -target %s",
"xclip -out -selection clipboard -target TARGETS",
"xclip -out -selection clipboard -target %s",
"Requires the xclip utility to be installed",
SDL_SYSWM_X11,
} },
{ "wl-clipboard", {
"wl-copy --type %s",
"wl-paste --list-types",
"wl-paste --type %s",
"Requires the wl-clipboard utility to be installed",
SDL_SYSWM_WAYLAND,
} },
};
@ -98,6 +101,7 @@ namespace Clipboard
SubstFormat(it->second.inCommand),
SubstFormat(it->second.formatsCommand),
SubstFormat(it->second.outCommand),
it->second.explanation,
};
}
@ -240,6 +244,12 @@ namespace Clipboard
}
return GetClipboardDataChanged{ std::move(*saveDataOpt) };
}
std::optional<String> Explanation() final override
{
auto preset = GetPreset();
return preset ? preset->explanation : std::nullopt;
}
};
std::unique_ptr<ClipboardImpl> ExternalClipboardFactory()

View File

@ -16,7 +16,21 @@ namespace Clipboard
{
}
bool GetEnabled()
{
return false;
}
void SetEnabled(bool)
{
}
void RecreateWindow()
{
}
std::optional<String> Explanation()
{
return std::nullopt;
}
}

View File

@ -217,6 +217,11 @@ namespace Clipboard
auto base = reinterpret_cast<const char *>(data.get());
return GetClipboardDataChanged{ std::vector<char>(base, base + size) };
}
std::optional<String> Explanation() final override
{
return std::nullopt;
}
};
std::unique_ptr<ClipboardImpl> WindowsClipboardFactory()

View File

@ -1,4 +1,4 @@
if get_option('platform_clipboard')
if platform_clipboard
clipboard_impl_factories = []
if host_platform == 'windows'
powder_files += files('Windows.cpp')

View File

@ -1,7 +1,6 @@
common_files += files(
'String.cpp',
'tpt-rand.cpp',
'tpt-thread-local.cpp',
)
subdir('clipboard')

View File

@ -58,7 +58,7 @@ bool ReadFile(std::vector<char> &fileData, ByteString filename)
if (f) f.seekg(0, std::ios::end);
if (f) fileData.resize(f.tellg());
if (f) f.seekg(0);
if (f) f.read(&fileData[0], fileData.size());
if (f && fileData.size()) f.read(&fileData[0], fileData.size());
if (!f)
{
std::cerr << "ReadFile: " << filename << ": " << strerror(errno) << std::endl;

View File

@ -6,6 +6,9 @@
#include "Config.h"
#include <cstring>
#include <ctime>
#ifdef __FreeBSD__
# include <sys/sysctl.h>
#endif
namespace Platform
{
@ -26,7 +29,26 @@ long unsigned int GetTime()
ByteString ExecutableNameFirstApprox()
{
return "/proc/self/exe";
if (Stat("/proc/self/exe"))
{
return "/proc/self/exe";
}
#ifdef __FreeBSD__
{
int mib[4];
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PATHNAME;
mib[3] = -1;
std::array<char, 1000> buf;
size_t cb = buf.size();
if (!sysctl(mib, 4, &buf[0], &cb, NULL, 0))
{
return ByteString(&buf[0], &buf[0] + cb);
}
}
#endif
return "";
}
bool CanUpdate()

View File

@ -1,6 +1,9 @@
#pragma once
#include "common/String.h"
#include <cstdint>
#include <string>
#include <vector>
#include <optional>
namespace Platform
{
@ -67,6 +70,8 @@ namespace Platform
int InvokeMain(int argc, char *argv[]);
std::optional<std::vector<String>> StackTrace();
void MarkPresentable();
}

View File

@ -2,7 +2,11 @@ common_files += files(
'Common.cpp',
)
use_bluescreen = not is_debug
if get_option('use_bluescreen') == 'auto'
use_bluescreen = not is_debug
else
use_bluescreen = get_option('use_bluescreen') == 'yes'
endif
can_install_enforce_no = false
set_window_icon = false
path_sep_char = '/'
@ -73,6 +77,19 @@ else
'DdirCommon.cpp',
)
endif
bluescreen_export_symbols = false
subdir('stacktrace')
if use_bluescreen
common_files += stacktrace_files
if bluescreen_export_symbols and is_debug
project_export_dynamic = true
endif
else
common_files += files('stacktrace/Null.cpp')
endif
conf_data.set('SET_WINDOW_ICON', set_window_icon.to_string())
conf_data.set('PATH_SEP_CHAR', path_sep_char)
conf_data.set('USE_BLUESCREEN', use_bluescreen.to_string())

View File

@ -0,0 +1,53 @@
#include "common/platform/Platform.h"
#include "common/Defer.h"
#include <execinfo.h>
#include <cstdint>
#include <array>
#include <cxxabi.h>
#include <iostream>
namespace Platform
{
std::optional<std::vector<String>> StackTrace()
{
std::array<void *, 100> buf;
auto used = backtrace(&buf[0], buf.size());
auto *strs = backtrace_symbols(&buf[0], used);
Defer freeStrs([strs]() {
free(strs);
});
std::vector<String> res;
for (auto i = 0; i < used; ++i)
{
if (strs)
{
auto line = ByteString(strs[i]);
if (auto beginSymbolName = line.SplitBy('('))
{
auto afterBeginSymbolName = beginSymbolName.After();
if (auto endSymbolName = afterBeginSymbolName.SplitBy('+'))
{
auto beforeSymbolName = beginSymbolName.Before();
auto symbolName = endSymbolName.Before();
auto afterSymbolName = endSymbolName.After();
int status;
char *demangled = abi::__cxa_demangle(symbolName.c_str(), NULL, NULL, &status);
Defer freeDemangled([demangled]() {
free(demangled);
});
if (!status)
{
line = ByteString::Build(beforeSymbolName, "(", demangled, "+", afterSymbolName);
}
}
}
res.push_back(line.FromUtf8());
}
else
{
res.push_back(String::Build("0x", Format::Hex(), uintptr_t(buf[i])));
}
}
return res;
}
}

View File

@ -0,0 +1,9 @@
#include "common/platform/Platform.h"
namespace Platform
{
std::optional<std::vector<String>> StackTrace()
{
return std::nullopt;
}
}

View File

@ -0,0 +1,137 @@
#include "common/platform/Platform.h"
#include "common/Defer.h"
#include <windows.h>
#pragma pack(push, 8)
#include <dbghelp.h>
#pragma pack(pop)
#include <psapi.h>
#include <array>
#include <mutex>
#include <cstdint>
namespace Platform
{
struct SymbolInfo
{
String name;
uintptr_t displacement;
};
static std::optional<SymbolInfo> GetSymbolInfo(HANDLE process, uintptr_t offset)
{
DWORD64 displacement;
std::array<char, sizeof(SYMBOL_INFOW) + 1000> symbolData{};
auto &symbol = *reinterpret_cast<SYMBOL_INFOW *>(&symbolData[0]);
symbol.SizeOfStruct = sizeof(symbol);
symbol.MaxNameLen = symbolData.size() - sizeof(symbol);
if (SymFromAddrW(process, offset, &displacement, &symbol))
{
return SymbolInfo{ WinNarrow(&symbol.Name[0]).FromUtf8(), uintptr_t(displacement) };
}
return std::nullopt;
}
struct ModuleInfo
{
String name;
uintptr_t displacement;
};
static std::optional<ModuleInfo> GetModuleInfo(HANDLE process, uintptr_t offset)
{
IMAGEHLP_MODULEW64 module{};
module.SizeOfStruct = sizeof(module);
if (SymGetModuleInfoW64(process, offset, &module))
{
auto displacement = offset - uintptr_t(module.BaseOfImage);
return ModuleInfo{ WinNarrow(&module.LoadedImageName[0]).FromUtf8(), displacement };
}
return std::nullopt;
}
std::optional<std::vector<String>> StackTrace()
{
static std::mutex mx;
std::unique_lock lk(mx);
auto process = GetCurrentProcess();
auto thread = GetCurrentThread();
Defer symCleanup([process]() {
SymCleanup(process);
});
SymInitialize(process, NULL, TRUE);
CONTEXT context{};
context.ContextFlags = CONTEXT_FULL;
RtlCaptureContext(&context);
STACKFRAME64 frame{};
DWORD machine;
#if defined(_M_IX86)
machine = IMAGE_FILE_MACHINE_I386;
frame.AddrPC.Offset = context.Eip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Ebp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Esp;
frame.AddrStack.Mode = AddrModeFlat;
#elif defined(_M_X64)
machine = IMAGE_FILE_MACHINE_AMD64;
frame.AddrPC.Offset = context.Rip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Rsp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Rsp;
frame.AddrStack.Mode = AddrModeFlat;
#elif defined(_M_IA64)
machine = IMAGE_FILE_MACHINE_IA64;
frame.AddrPC.Offset = context.StIIP;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.IntSp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrBStore.Offset = context.RsBSP;
frame.AddrBStore.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.IntSp;
frame.AddrStack.Mode = AddrModeFlat;
#elif defined(_M_ARM64)
machine = IMAGE_FILE_MACHINE_ARM64;
frame.AddrPC.Offset = context.Pc;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Fp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Sp;
frame.AddrStack.Mode = AddrModeFlat;
#else
return std::nullopt;
#endif
std::vector<String> res;
for (auto i = 0; i < 100; ++i)
{
if (!StackWalk64(machine, process, thread, &frame, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
{
break;
}
auto offset = uintptr_t(frame.AddrPC.Offset);
StringBuilder addr;
addr << Format::Hex();
if (auto moduleInfo = GetModuleInfo(process, offset))
{
addr << moduleInfo->name << "(";
if (auto symbolInfo = GetSymbolInfo(process, offset))
{
addr << symbolInfo->name << "+0x" << symbolInfo->displacement;
}
else
{
addr << "+0x" << moduleInfo->displacement;
}
addr << ") [0x" << offset << "]";
}
else
{
addr << "0x" << offset;
}
res.push_back(addr.Build());
}
return res;
}
}

View File

@ -0,0 +1,26 @@
if host_platform == 'windows'
if use_bluescreen
project_deps += [
c_compiler.find_library('dbghelp'),
c_compiler.find_library('psapi'),
]
endif
stacktrace_files = files('Windows.cpp')
elif host_platform == 'darwin'
# TODO: good impl; current one is only slightly better than nothing
stacktrace_files = files('Execinfo.cpp')
# export symbols so backtrace_symbols works, see https://www.gnu.org/software/libc/manual/html_node/Backtraces.html
bluescreen_export_symbols = true
elif host_platform == 'linux'
# TODO: again, this is more like "posix" than "linux"
stacktrace_files = files('Execinfo.cpp')
# export symbols so backtrace_symbols works, see above
bluescreen_export_symbols = true
if host_machine.system() in [ 'freebsd' ]
project_deps += [
c_compiler.find_library('execinfo'),
]
endif
else
stacktrace_files = files('Null.cpp')
endif

View File

@ -1,61 +0,0 @@
#include "tpt-thread-local.h"
#ifdef __MINGW32__
# include <pthread.h>
# include <cstdlib>
# include <cassert>
void *ThreadLocalCommon::Get() const
{
// https://stackoverflow.com/questions/16552710/how-do-you-get-the-start-and-end-addresses-of-a-custom-elf-section
extern ThreadLocalCommon __start_tpt_tls;
extern ThreadLocalCommon __stop_tpt_tls;
static pthread_once_t once = PTHREAD_ONCE_INIT;
static pthread_key_t key;
struct ThreadLocalEntry
{
void *ptr;
};
auto *staticsBegin = &__start_tpt_tls;
auto *staticsEnd = &__stop_tpt_tls;
pthread_once(&once, []() -> void {
assert(!pthread_key_create(&key, [](void *opaque) -> void {
auto *staticsBegin = &__start_tpt_tls;
auto *staticsEnd = &__stop_tpt_tls;
auto staticsCount = staticsEnd - staticsBegin;
auto *liveObjects = reinterpret_cast<ThreadLocalEntry *>(opaque);
if (liveObjects)
{
for (auto i = 0; i < staticsCount; ++i)
{
if (liveObjects[i].ptr)
{
staticsBegin[i].dtor(liveObjects[i].ptr);
free(liveObjects[i].ptr);
}
}
free(liveObjects);
}
}));
});
auto *liveObjects = reinterpret_cast<ThreadLocalEntry *>(pthread_getspecific(key));
if (!liveObjects)
{
auto staticsCount = staticsEnd - staticsBegin;
liveObjects = reinterpret_cast<ThreadLocalEntry *>(calloc(staticsCount, sizeof(ThreadLocalEntry)));
assert(liveObjects);
assert(!pthread_setspecific(key, reinterpret_cast<void *>(liveObjects)));
}
auto idx = this - staticsBegin;
auto &entry = liveObjects[idx];
if (!entry.ptr)
{
entry.ptr = malloc(staticsBegin[idx].size);
assert(entry.ptr);
staticsBegin[idx].ctor(entry.ptr);
}
return entry.ptr;
}
#endif

View File

@ -1,63 +0,0 @@
#pragma once
#ifdef __MINGW32__
# include <cstddef>
class ThreadLocalCommon
{
ThreadLocalCommon(const ThreadLocalCommon &other) = delete;
ThreadLocalCommon &operator =(const ThreadLocalCommon &other) = delete;
protected:
size_t size;
void (*ctor)(void *);
void (*dtor)(void *);
size_t padding;
void *Get() const;
public:
ThreadLocalCommon() = default;
static constexpr size_t Alignment = 0x20;
};
// * If this fails, add or remove padding fields, possibly change Alignment to a larger power of 2.
static_assert(sizeof(ThreadLocalCommon) == ThreadLocalCommon::Alignment, "fix me");
template<class Type>
class ThreadLocal : public ThreadLocalCommon
{
static void Ctor(void *type)
{
new(type) Type();
}
static void Dtor(void *type)
{
reinterpret_cast<Type *>(type)->~Type();
}
public:
ThreadLocal()
{
// * If this fails, you're out of luck.
static_assert(sizeof(ThreadLocal<Type>) == sizeof(ThreadLocalCommon), "fix me");
size = sizeof(Type);
ctor = Ctor;
dtor = Dtor;
}
Type *operator &() const
{
return reinterpret_cast<Type *>(Get());
}
operator Type &() const
{
return *(this->operator &());
}
};
# define THREAD_LOCAL(Type, tl) const ThreadLocal<Type> tl __attribute__((section("tpt_tls"), aligned(ThreadLocalCommon::Alignment)))
#else
# define THREAD_LOCAL(Type, tl) thread_local Type tl
#endif

View File

@ -183,6 +183,8 @@ void Renderer::render_parts()
gfctx.pipeSubcallTpart = nullptr;
int deca, decr, decg, decb, cola, colr, colg, colb, firea, firer, fireg, fireb, pixel_mode, q, i, t, nx, ny, x, y;
int orbd[4] = {0, 0, 0, 0}, orbl[4] = {0, 0, 0, 0};
int drawing_budget = 1000000; //Serves as an upper bound for costly effects such as SPARK, FLARE and LFLARE
if(!sim)
return;
auto *parts = sim->parts;
@ -633,7 +635,7 @@ void Renderer::render_parts()
{
auto flicker = float(gfctx.rng()%20);
auto gradv = 4*sim->parts[i].life + flicker;
for (x = 0; gradv>0.5; x++) {
for (x = 0; (gradv>0.5) && (drawing_budget > 0); x++) {
auto col = RGBA<uint8_t>(
std::min(0xFF, colr * int(gradv) / 255),
std::min(0xFF, colg * int(gradv) / 255),
@ -644,6 +646,7 @@ void Renderer::render_parts()
AddPixel({ nx, ny+x }, col);
AddPixel({ nx, ny-x }, col);
gradv = gradv/1.5f;
drawing_budget--;
}
}
if(pixel_mode & PMODE_FLARE)
@ -660,12 +663,13 @@ void Renderer::render_parts()
BlendPixel({ nx-1, ny-1 }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
BlendPixel({ nx+1, ny+1 }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
BlendPixel({ nx-1, ny+1 }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
for (x = 1; gradv>0.5; x++) {
for (x = 1; (gradv>0.5) && (drawing_budget > 0); x++) {
AddPixel({ nx+x, ny }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
AddPixel({ nx-x, ny }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
AddPixel({ nx, ny+x }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
AddPixel({ nx, ny-x }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
gradv = gradv/1.2f;
drawing_budget--;
}
}
if(pixel_mode & PMODE_LFLARE)
@ -682,12 +686,13 @@ void Renderer::render_parts()
BlendPixel({ nx-1, ny-1 }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
BlendPixel({ nx+1, ny+1 }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
BlendPixel({ nx-1, ny+1 }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
for (x = 1; gradv>0.5; x++) {
for (x = 1; (gradv>0.5) && (drawing_budget > 0); x++) {
AddPixel({ nx+x, ny }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
AddPixel({ nx-x, ny }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
AddPixel({ nx, ny+x }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
AddPixel({ nx, ny-x }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
gradv = gradv/1.01f;
drawing_budget--;
}
}
if (pixel_mode & EFFECT_GRAVIN)

View File

@ -41,6 +41,8 @@ class Renderer: public RasterDrawMethods<Renderer>
friend struct RasterDrawMethods<Renderer>;
float fireIntensity = 1;
public:
Vec2<int> Size() const
{
@ -97,6 +99,10 @@ public:
void DrawSigns();
void render_gravlensing(const Video &source);
void render_fire();
float GetFireIntensity() const
{
return fireIntensity;
}
void prepare_alpha(int size, float intensity);
void render_parts();
void draw_grav_zones();

View File

@ -11,7 +11,6 @@ constexpr auto VIDYRES = WINDOWH;
void Renderer::RenderBegin()
{
draw_air();
draw_grav();
DrawWalls();
render_parts();
@ -142,9 +141,10 @@ float glow_alphaf[11][11];
float blur_alphaf[7][7];
void Renderer::prepare_alpha(int size, float intensity)
{
fireIntensity = intensity;
//TODO: implement size
int x,y,i,j;
float multiplier = 255.0f*intensity;
float multiplier = 255.0f*fireIntensity;
memset(temp, 0, sizeof(temp));
for (x=0; x<CELL; x++)

View File

@ -6,6 +6,7 @@
#include <SDL.h>
#include "gui/interface/Textbox.h"
#include "gui/interface/ScrollPanel.h"
#include "gui/interface/Label.h"
#include "gui/game/Tool.h"
#include "gui/game/Menu.h"
@ -52,6 +53,9 @@ ElementSearchActivity::ElementSearchActivity(GameController * gameController, st
AddComponent(okButton);
AddComponent(closeButton);
scrollPanel = new ui::ScrollPanel(searchField->Position + Vec2{ 1, searchField->Size.Y+9 }, { searchField->Size.X - 2, Size.Y-(searchField->Position.Y+searchField->Size.Y+6)-23 });
AddComponent(scrollPanel);
searchTools("");
}
@ -59,12 +63,12 @@ void ElementSearchActivity::searchTools(String query)
{
firstResult = NULL;
for (auto &toolButton : toolButtons) {
RemoveComponent(toolButton);
scrollPanel->RemoveChild(toolButton);
delete toolButton;
}
toolButtons.clear();
ui::Point viewPosition = searchField->Position + ui::Point(2+0, searchField->Size.Y+2+8);
ui::Point viewPosition = { 1, 1 };
ui::Point current = ui::Point(0, 0);
String queryLower = query.ToLower();
@ -178,7 +182,7 @@ void ElementSearchActivity::searchTools(String query)
}
toolButtons.push_back(tempButton);
AddComponent(tempButton);
scrollPanel->AddChild(tempButton);
current.X += 31;
@ -186,10 +190,13 @@ void ElementSearchActivity::searchTools(String query)
current.X = 0;
current.Y += 19;
}
if(current.Y + viewPosition.Y + 18 > Size.Y-23)
break;
}
if (current.X == 0)
{
current.Y -= 19;
}
scrollPanel->InnerSize = ui::Point(scrollPanel->Size.X, current.Y + 20);
}
void ElementSearchActivity::SetActiveTool(int selectionState, Tool * tool)
@ -216,8 +223,7 @@ void ElementSearchActivity::OnDraw()
g->DrawRect(RectSized(Position, Size), 0xFFFFFF_rgb);
g->BlendRect(
RectSized(Position + searchField->Position + Vec2{ 0, searchField->Size.Y+8 },
{ searchField->Size.X, Size.Y-(searchField->Position.Y+searchField->Size.Y+8)-23 }),
RectSized(Position + scrollPanel->Position - Vec2{ 1, 1 }, scrollPanel->Size + Vec2{ 2, 2 }),
0xFFFFFF_rgb .WithAlpha(180));
if (toolTipPresence && toolTip.length())
{

View File

@ -10,6 +10,7 @@ class GameController;
namespace ui
{
class ScrollPanel;
class Textbox;
}
@ -20,6 +21,7 @@ class ElementSearchActivity: public WindowActivity
std::vector<Tool*> tools;
ui::Textbox * searchField;
std::vector<ToolButton*> toolButtons;
ui::ScrollPanel *scrollPanel = nullptr;
String toolTip;
int toolTipPresence;
bool shiftPressed;

View File

@ -39,7 +39,7 @@ class LoadFilesTask: public Task
bool doWork() override
{
std::vector<ByteString> files = Platform::DirectorySearch(directory, search, { ".cps" });
std::sort(files.rbegin(), files.rend(), [](ByteString a, ByteString b) { return a.ToLower() < b.ToLower(); });
std::sort(files.rbegin(), files.rend(), [](ByteString a, ByteString b) { return a.ToLower() > b.ToLower(); });
notifyProgress(-1);
for(std::vector<ByteString>::iterator iter = files.begin(), end = files.end(); iter != end; ++iter)

View File

@ -52,6 +52,14 @@ void Brush::InitOutline()
void Brush::SetRadius(ui::Point newRadius)
{
if (newRadius.X < 0)
newRadius.X = 0;
if (newRadius.Y < 0)
newRadius.Y = 0;
if (newRadius.X > 200)
newRadius.X = 200;
if (newRadius.Y > 200)
newRadius.Y = 200;
radius = newRadius;
InitOutline();
}
@ -67,14 +75,6 @@ void Brush::AdjustSize(int delta, bool logarithmic, bool keepX, bool keepY)
newSize = oldSize + ui::Point(delta * std::max(oldSize.X / 5, 1), delta * std::max(oldSize.Y / 5, 1));
else
newSize = oldSize + ui::Point(delta, delta);
if (newSize.X < 0)
newSize.X = 0;
if (newSize.Y < 0)
newSize.Y = 0;
if (newSize.X > 200)
newSize.X = 200;
if (newSize.Y > 200)
newSize.Y = 200;
if (keepY)
SetRadius(ui::Point(newSize.X, oldSize.Y));

View File

@ -90,7 +90,7 @@ GameController::GameController():
gameView->SetDebugHUD(GlobalPrefs::Ref().Get("Renderer.DebugMode", false));
CommandInterface::Create(this, gameModel);
commandInterface = CommandInterface::Create(this, gameModel);
Client::Ref().AddListener(this);
@ -146,7 +146,7 @@ GameController::~GameController()
{
delete *iter;
}
delete commandInterface;
commandInterface.reset();
delete gameModel;
if (gameView->CloseActiveWindow())
{
@ -396,6 +396,10 @@ void GameController::DrawPoints(int toolSelection, ui::Point oldPos, ui::Point n
}
activeTool->Strength = gameModel->GetToolStrength();
// This is a joke, the game mvc has to go >_>
activeTool->shiftBehaviour = gameView->ShiftBehaviour();
activeTool->ctrlBehaviour = gameView->CtrlBehaviour();
activeTool->altBehaviour = gameView->AltBehaviour();
if (!held)
activeTool->Draw(sim, cBrush, newPos);
else
@ -444,7 +448,12 @@ static Rect<int> SaneSaveRect(Vec2<int> point1, Vec2<int> point2)
ByteString GameController::StampRegion(ui::Point point1, ui::Point point2)
{
auto newSave = gameModel->GetSimulation()->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour(), SaneSaveRect(point1, point2));
return StampRegion(point1, point2, gameModel->GetIncludePressure() != gameView->ShiftBehaviour());
}
ByteString GameController::StampRegion(ui::Point point1, ui::Point point2, bool includePressure)
{
auto newSave = gameModel->GetSimulation()->Save(includePressure, SaneSaveRect(point1, point2));
if(newSave)
{
newSave->paused = gameModel->GetPaused();
@ -543,7 +552,7 @@ bool GameController::MouseUp(int x, int y, unsigned button, MouseupReason reason
}
break;
case sign::Type::Thread:
Platform::OpenURI(ByteString::Build(SCHEME, "powdertoy.co.uk/Discussions/Thread/View.html?Thread=", str.Substr(3, si.first - 3).ToUtf8()));
Platform::OpenURI(ByteString::Build(SCHEME, SERVER, "/Discussions/Thread/View.html?Thread=", str.Substr(3, si.first - 3).ToUtf8()));
break;
case sign::Type::Search:
OpenSearch(str.Substr(3, si.first - 3));
@ -776,7 +785,7 @@ void GameController::ResetSpark()
void GameController::SwitchGravity()
{
gameModel->GetSimulation()->gravityMode = (gameModel->GetSimulation()->gravityMode + 1) % NUM_GRAV_MODES;
gameModel->GetSimulation()->gravityMode = (gameModel->GetSimulation()->gravityMode + 1) % NUM_GRAVMODES;
switch (gameModel->GetSimulation()->gravityMode)
{
@ -797,23 +806,23 @@ void GameController::SwitchGravity()
void GameController::SwitchAir()
{
gameModel->GetSimulation()->air->airMode = (gameModel->GetSimulation()->air->airMode + 1) % NUM_AIR_MODES;
gameModel->GetSimulation()->air->airMode = (gameModel->GetSimulation()->air->airMode + 1) % NUM_AIRMODES;
switch (gameModel->GetSimulation()->air->airMode)
{
case AIR_ON:
gameModel->SetInfoTip("Air: On");
break;
case AIR_PRESSURE_OFF:
case AIR_PRESSUREOFF:
gameModel->SetInfoTip("Air: Pressure Off");
break;
case AIR_VELOCITY_OFF:
case AIR_VELOCITYOFF:
gameModel->SetInfoTip("Air: Velocity Off");
break;
case AIR_OFF:
gameModel->SetInfoTip("Air: Off");
break;
case AIR_NO_UPDATE:
case AIR_NOUPDATE:
gameModel->SetInfoTip("Air: No Update");
break;
}
@ -1022,7 +1031,7 @@ int GameController::GetEdgeMode()
void GameController::SetEdgeMode(int edgeMode)
{
if (edgeMode < 0 || edgeMode >= NUM_EDGE_MODES)
if (edgeMode < 0 || edgeMode >= NUM_EDGEMODES)
edgeMode = 0;
gameModel->SetEdgeMode(edgeMode);
@ -1102,11 +1111,16 @@ void GameController::SetActiveTool(int toolSelection, Tool * tool)
gameModel->SetLastTool(tool);
for(int i = 0; i < 3; i++)
{
if(gameModel->GetActiveTool(i) == gameModel->GetMenuList().at(SC_WALL)->GetToolList().at(WL_GRAV))
auto *activeTool = gameModel->GetActiveTool(i);
if (activeTool && activeTool->Identifier == "DEFAULT_WL_GRVTY")
{
gameModel->GetRenderer()->gravityZonesEnabled = true;
}
}
if (tool->Identifier == "DEFAULT_UI_PROPERTY")
{
static_cast<PropertyTool *>(tool)->OpenWindow(gameModel->GetSimulation(), nullptr);
}
if(tool->Identifier == "DEFAULT_UI_PROPERTY")
((PropertyTool *)tool)->OpenWindow(gameModel->GetSimulation());
if(tool->Identifier == "DEFAULT_UI_ADDLIFE")
{
((GOLTool *)tool)->OpenWindow(gameModel->GetSimulation(), toolSelection);
@ -1126,6 +1140,11 @@ void GameController::SetLastTool(Tool * tool)
gameModel->SetLastTool(tool);
}
Tool *GameController::GetLastTool()
{
return gameModel->GetLastTool();
}
int GameController::GetReplaceModeFlags()
{
return gameModel->GetSimulation()->replaceModeFlags;
@ -1370,7 +1389,7 @@ void GameController::OpenOptions()
void GameController::ShowConsole()
{
if (!console)
console = new ConsoleController(NULL, commandInterface);
console = new ConsoleController(NULL, commandInterface.get());
if (console->GetView() != ui::Engine::Ref().GetWindow())
ui::Engine::Ref().ShowWindow(console->GetView());
}
@ -1697,3 +1716,13 @@ void GameController::RemoveCustomGOLType(const ByteString &identifier)
{
gameModel->RemoveCustomGOLType(identifier);
}
void GameController::BeforeSimDraw()
{
commandInterface->HandleEvent(BeforeSimDrawEvent{});
}
void GameController::AfterSimDraw()
{
commandInterface->HandleEvent(AfterSimDrawEvent{});
}

View File

@ -1,4 +1,5 @@
#pragma once
#include "lua/CommandInterfacePtr.h"
#include "client/ClientListener.h"
#include "client/StartupInfo.h"
#include "gui/interface/Point.h"
@ -28,7 +29,6 @@ class LocalBrowserController;
class SearchController;
class PreviewController;
class RenderController;
class CommandInterface;
class VideoBuffer;
class Tool;
class Menu;
@ -39,6 +39,8 @@ class TagsController;
class ConsoleController;
class GameController: public ClientListener
{
CommandInterfacePtr commandInterface;
private:
bool firstTick;
int foundSignID;
@ -106,6 +108,7 @@ public:
void DrawLine(int toolSelection, ui::Point point1, ui::Point point2);
void DrawFill(int toolSelection, ui::Point point);
ByteString StampRegion(ui::Point point1, ui::Point point2);
ByteString StampRegion(ui::Point point1, ui::Point point2, bool includePressure);
void CopyRegion(ui::Point point1, ui::Point point2);
void CutRegion(ui::Point point1, ui::Point point2);
void Update();
@ -125,6 +128,7 @@ public:
int GetEdgeMode();
void SetEdgeMode(int edgeMode);
void SetDebugFlags(unsigned int flags) { debugFlags = flags; }
unsigned int GetDebugFlags() const { return debugFlags; }
void SetActiveMenu(int menuID);
std::vector<Menu*> GetMenuList();
int GetNumMenus(bool onlyEnabled);
@ -133,6 +137,7 @@ public:
void SetActiveTool(int toolSelection, Tool * tool);
void SetActiveTool(int toolSelection, ByteString identifier);
void SetLastTool(Tool * tool);
Tool *GetLastTool();
int GetReplaceModeFlags();
void SetReplaceModeFlags(int flags);
void SetActiveColourPreset(int preset);
@ -196,4 +201,7 @@ public:
bool GetMouseClickRequired();
void RemoveCustomGOLType(const ByteString &identifier);
void BeforeSimDraw();
void AfterSimDraw();
};

View File

@ -1,22 +1,30 @@
#pragma once
#include "common/String.h"
#include <variant>
#include <cstdint>
enum EventTraits : uint32_t
{
eventTraitNone = UINT32_C(0x00000000),
eventTraitSimRng = UINT32_C(0x00000001),
eventTraitSimGraphics = UINT32_C(0x00000002),
};
struct TextInputEvent
{
static constexpr bool simEvent = false;
static constexpr EventTraits traits = eventTraitNone;
String text;
};
struct TextEditingEvent
{
static constexpr bool simEvent = false;
static constexpr EventTraits traits = eventTraitNone;
String text;
};
struct KeyEvent
{
static constexpr bool simEvent = false;
static constexpr EventTraits traits = eventTraitNone;
int key;
int scan;
bool repeat;
@ -27,17 +35,17 @@ struct KeyEvent
struct KeyPressEvent : public KeyEvent
{
static constexpr bool simEvent = false;
static constexpr EventTraits traits = eventTraitNone;
};
struct KeyReleaseEvent : public KeyEvent
{
static constexpr bool simEvent = false;
static constexpr EventTraits traits = eventTraitNone;
};
struct MouseDownEvent
{
static constexpr bool simEvent = false;
static constexpr EventTraits traits = eventTraitNone;
int x;
int y;
unsigned int button;
@ -45,7 +53,7 @@ struct MouseDownEvent
struct MouseUpEvent
{
static constexpr bool simEvent = false;
static constexpr EventTraits traits = eventTraitNone;
int x;
int y;
unsigned int button;
@ -54,7 +62,7 @@ struct MouseUpEvent
struct MouseMoveEvent
{
static constexpr bool simEvent = false;
static constexpr EventTraits traits = eventTraitNone;
int x;
int y;
int dx;
@ -63,7 +71,7 @@ struct MouseMoveEvent
struct MouseWheelEvent
{
static constexpr bool simEvent = false;
static constexpr EventTraits traits = eventTraitNone;
int x;
int y;
int d;
@ -71,27 +79,37 @@ struct MouseWheelEvent
struct TickEvent
{
static constexpr bool simEvent = false;
static constexpr EventTraits traits = eventTraitNone;
};
struct BlurEvent
{
static constexpr bool simEvent = false;
static constexpr EventTraits traits = eventTraitNone;
};
struct CloseEvent
{
static constexpr bool simEvent = false;
static constexpr EventTraits traits = eventTraitNone;
};
struct BeforeSimEvent
{
static constexpr bool simEvent = true;
static constexpr EventTraits traits = eventTraitSimRng;
};
struct AfterSimEvent
{
static constexpr bool simEvent = true;
static constexpr EventTraits traits = eventTraitSimRng;
};
struct BeforeSimDrawEvent
{
static constexpr EventTraits traits = eventTraitSimGraphics;
};
struct AfterSimDrawEvent
{
static constexpr EventTraits traits = eventTraitSimGraphics;
};
using GameControllerEvent = std::variant<
@ -107,5 +125,7 @@ using GameControllerEvent = std::variant<
BlurEvent,
CloseEvent,
BeforeSimEvent,
AfterSimEvent
AfterSimEvent,
BeforeSimDrawEvent,
AfterSimDrawEvent
>;

View File

@ -54,16 +54,16 @@ GameModel::GameModel():
colour(255, 0, 0, 255),
edgeMode(EDGE_VOID),
ambientAirTemp(R_TEMP + 273.15f),
decoSpace(0)
decoSpace(DECOSPACE_SRGB)
{
sim = new Simulation();
sim->useLuaCallbacks = true;
ren = new Renderer(sim);
activeTools = regularToolset;
activeTools = &regularToolset[0];
std::fill(decoToolset, decoToolset+4, (Tool*)NULL);
std::fill(regularToolset, regularToolset+4, (Tool*)NULL);
std::fill(decoToolset.begin(), decoToolset.end(), nullptr);
std::fill(regularToolset.begin(), regularToolset.end(), nullptr);
//Default render prefs
ren->SetRenderMode({
@ -93,7 +93,7 @@ GameModel::GameModel():
ren->decorations_enable = prefs.Get("Renderer.Decorations", true);
//Load config into simulation
edgeMode = prefs.Get("Simulation.EdgeMode", (int)EDGE_VOID);
edgeMode = prefs.Get("Simulation.EdgeMode", NUM_EDGEMODES, EDGE_VOID);
sim->SetEdgeMode(edgeMode);
ambientAirTemp = float(R_TEMP) + 273.15f;
{
@ -104,9 +104,9 @@ GameModel::GameModel():
}
}
sim->air->ambientAirTemp = ambientAirTemp;
decoSpace = prefs.Get("Simulation.DecoSpace", 0); // TODO: DecoSpace enum
decoSpace = prefs.Get("Simulation.DecoSpace", NUM_DECOSPACES, DECOSPACE_SRGB);
sim->SetDecoSpace(decoSpace);
int ngrav_enable = prefs.Get("Simulation.NewtonianGravity", 0); // TODO: NewtonianGravity enum
int ngrav_enable = prefs.Get("Simulation.NewtonianGravity", NUM_GRAVMODES, GRAV_VERTICAL);
if (ngrav_enable)
sim->grav->start_grav_async();
sim->aheat_enable = prefs.Get("Simulation.AmbientHeat", 0); // TODO: AmbientHeat enum
@ -228,7 +228,7 @@ void GameModel::BuildMenus()
if(activeMenu != -1)
lastMenu = activeMenu;
ByteString activeToolIdentifiers[4];
std::array<ByteString, NUM_TOOLINDICES> activeToolIdentifiers;
if(regularToolset[0])
activeToolIdentifiers[0] = regularToolset[0]->Identifier;
if(regularToolset[1])
@ -881,17 +881,17 @@ void GameModel::SetActiveMenu(int menuID)
if(menuID == SC_DECO)
{
if(activeTools != decoToolset)
if(activeTools != &decoToolset[0])
{
activeTools = decoToolset;
activeTools = &decoToolset[0];
notifyActiveToolsChanged();
}
}
else
{
if(activeTools != regularToolset)
if(activeTools != &regularToolset[0])
{
activeTools = regularToolset;
activeTools = &regularToolset[0];
notifyActiveToolsChanged();
}
}
@ -1712,7 +1712,7 @@ void GameModel::BeforeSim()
{
if (!sim->sys_pause || sim->framerender)
{
commandInterface->HandleEvent(BeforeSimEvent{});
CommandInterface::Ref().HandleEvent(BeforeSimEvent{});
}
sim->BeforeSim();
}
@ -1720,5 +1720,5 @@ void GameModel::BeforeSim()
void GameModel::AfterSim()
{
sim->AfterSim();
commandInterface->HandleEvent(AfterSimEvent{});
CommandInterface::Ref().HandleEvent(AfterSimEvent{});
}

View File

@ -6,6 +6,9 @@
#include <deque>
#include <memory>
#include <optional>
#include <array>
constexpr auto NUM_TOOLINDICES = 4;
class Menu;
class Tool;
@ -72,8 +75,8 @@ private:
std::unique_ptr<SaveFile> currentFile;
Tool * lastTool;
Tool ** activeTools;
Tool * decoToolset[4];
Tool * regularToolset[4];
std::array<Tool *, NUM_TOOLINDICES> decoToolset;
std::array<Tool *, NUM_TOOLINDICES> regularToolset;
User currentUser;
float toolStrength;
std::deque<HistoryEntry> history;
@ -192,6 +195,10 @@ public:
Brush &GetBrush();
Brush *GetBrushByID(int i);
int GetBrushID();
int BrushListSize() const
{
return int(brushList.size());
}
void SetBrushID(int i);
void SetVote(int direction);

View File

@ -87,7 +87,7 @@ public:
}
}
}
void OnMouseUnclick(int x, int y, unsigned int button) override
void OnMouseClick(int x, int y, unsigned int button) override
{
if(isButtonDown)
{
@ -96,7 +96,7 @@ public:
else if(rightDown)
DoRightAction();
}
ui::Button::OnMouseUnclick(x, y, button);
ui::Button::OnMouseClick(x, y, button);
}
void OnMouseHover(int x, int y) override
@ -120,15 +120,18 @@ public:
toolTip = newToolTip1;
toolTip2 = newToolTip2;
}
void OnMouseClick(int x, int y, unsigned int button) override
void OnMouseDown(int x, int y, unsigned int button) override
{
ui::Button::OnMouseClick(x, y, button);
rightDown = false;
leftDown = false;
if(x >= splitPosition)
rightDown = true;
else if(x < splitPosition)
leftDown = true;
ui::Button::OnMouseDown(x, y, button);
if (MouseDownInside)
{
rightDown = false;
leftDown = false;
if(x - Position.X >= splitPosition)
rightDown = true;
else if(x - Position.X < splitPosition)
leftDown = true;
}
}
void DoRightAction()
{
@ -1052,10 +1055,17 @@ void GameView::updateToolButtonScroll()
{
for (auto *button : toolButtons)
{
if (button->Position.X < x && button->Position.X + button->Size.X > x)
auto inside = button->Position.X < x && button->Position.X + button->Size.X > x;
if (inside && !button->MouseInside)
{
button->MouseInside = true;
button->OnMouseEnter(x, y);
else
}
if (!inside && button->MouseInside)
{
button->MouseInside = false;
button->OnMouseLeave(x, y);
}
}
}
}
@ -2086,6 +2096,11 @@ void GameView::UpdateDrawMode()
drawMode = DrawLine;
else
drawMode = DrawPoints;
// TODO: have tools decide on draw mode
if (c->GetLastTool() && c->GetLastTool()->Identifier == "DEFAULT_UI_SAMPLE")
{
drawMode = DrawPoints;
}
}
void GameView::UpdateToolStrength()
@ -2119,6 +2134,8 @@ void GameView::OnDraw()
auto &sd = SimulationData::Ref();
std::unique_lock lk(sd.elementGraphicsMx);
ren->clearScreen();
ren->draw_air();
c->BeforeSimDraw();
ren->RenderBegin();
ren->SetSample(c->PointTranslate(currentMouse));
if (showBrush && selectMode == SelectNone && (!zoomEnabled || zoomCursorFixed) && activeBrush && (isMouseDown || (currentMouse.X >= 0 && currentMouse.X < XRES && currentMouse.Y >= 0 && currentMouse.Y < YRES)))
@ -2224,6 +2241,7 @@ void GameView::OnDraw()
}
}
c->AfterSimDraw();
ren->RenderEnd();
std::copy_n(ren->Data(), ren->Size().X * ren->Size().Y, g->Data());
@ -2363,7 +2381,7 @@ void GameView::OnDraw()
if (type == PT_CRAY || type == PT_DRAY || type == PT_EXOT || type == PT_LIGH || type == PT_SOAP || type == PT_TRON
|| type == PT_VIBR || type == PT_VIRS || type == PT_WARP || type == PT_LCRY || type == PT_CBNW || type == PT_TSNS
|| type == PT_DTEC || type == PT_LSNS || type == PT_PSTN || type == PT_LDTC || type == PT_VSNS || type == PT_LITH
|| type == PT_CONV)
|| type == PT_CONV || type == PT_ETRD)
sampleInfo << ", Tmp2: " << sample.particle.tmp2;
sampleInfo << ", Pressure: " << sample.AirPressure;

View File

@ -76,7 +76,7 @@ inline ByteString IntroText()
}
else
{
sb << "\bgTo use online features such as saving, you need to register at: \brhttps://powdertoy.co.uk/Register.html\n";
sb << "\bgTo use online features such as saving, you need to register at: \brhttps://" << SERVER << "/Register.html\n";
}
sb << "\n\bt" << VersionInfo();
return sb.Build();

View File

@ -28,6 +28,7 @@
class PropertyWindow: public ui::Window
{
void HandlePropertyChange();
std::optional<std::pair<int, String>> TakePropertyFrom(const Particle *takePropertyFrom) const;
public:
ui::DropDown * property;
@ -36,7 +37,7 @@ public:
Simulation *sim;
std::vector<StructProperty> properties;
std::optional<PropertyTool::Configuration> configuration;
PropertyWindow(PropertyTool *tool_, Simulation *sim);
PropertyWindow(PropertyTool *tool_, Simulation *sim, const Particle *takePropertyFrom);
void SetProperty();
void CheckProperty();
void Update();
@ -46,7 +47,7 @@ public:
virtual ~PropertyWindow() {}
};
PropertyWindow::PropertyWindow(PropertyTool * tool_, Simulation *sim_):
PropertyWindow::PropertyWindow(PropertyTool * tool_, Simulation *sim_, const Particle *takePropertyFrom):
ui::Window(ui::Point(-1, -1), ui::Point(200, 87)),
tool(tool_),
sim(sim_)
@ -81,23 +82,67 @@ sim(sim_)
property->AddOption(std::pair<String, int>(properties[i].Name.FromAscii(), i));
}
auto &prefs = GlobalPrefs::Ref();
property->SetOption(prefs.Get("Prop.Type", 0));
textField = new ui::Textbox(ui::Point(8, 46), ui::Point(Size.X-16, 16), "", "[value]");
textField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
textField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
textField->SetText(prefs.Get("Prop.Value", String("")));
textField->SetActionCallback({ [this]() {
Update();
} });
AddComponent(textField);
{
auto &prefs = GlobalPrefs::Ref();
auto propertyIndex = prefs.Get("Prop.Type", 0);
auto valueString = prefs.Get("Prop.Value", String(""));
auto taken = TakePropertyFrom(takePropertyFrom);
if (taken)
{
std::tie(propertyIndex, valueString) = *taken;
}
property->SetOption(propertyIndex);
textField->SetText(valueString);
}
FocusComponent(textField);
Update();
MakeActiveWindow();
}
std::optional<std::pair<int, String>> PropertyWindow::TakePropertyFrom(const Particle *takePropertyFrom) const
{
auto toolConfiguration = tool->GetConfiguration();
if (!toolConfiguration || !takePropertyFrom)
{
return {};
}
auto *prop = reinterpret_cast<const char *>(takePropertyFrom) + toolConfiguration->prop.Offset;
auto takeValue = [this, toolConfiguration](auto &value) -> std::optional<std::pair<int, String>> {
auto it = std::find(properties.begin(), properties.end(), toolConfiguration->prop);
if (it != properties.end())
{
return std::pair{ int(it - properties.begin()), String::Build(value) };
}
return {};
};
switch (toolConfiguration->prop.Type)
{
case StructProperty::Float:
return takeValue(*reinterpret_cast<const float *>(prop));
case StructProperty::ParticleType:
case StructProperty::Integer:
return takeValue(*reinterpret_cast<const int *>(prop));
case StructProperty::UInteger:
return takeValue(*reinterpret_cast<const unsigned int *>(prop));
default:
break;
}
return {};
}
void PropertyWindow::HandlePropertyChange()
{
FocusComponent(textField);
@ -287,9 +332,9 @@ void PropertyWindow::OnKeyPress(int key, int scan, bool repeat, bool shift, bool
}
}
void PropertyTool::OpenWindow(Simulation *sim)
void PropertyTool::OpenWindow(Simulation *sim, const Particle *takePropertyFrom)
{
new PropertyWindow(this, sim);
new PropertyWindow(this, sim, takePropertyFrom);
}
void PropertyTool::SetProperty(Simulation *sim, ui::Point position)
@ -325,39 +370,6 @@ void PropertyTool::SetProperty(Simulation *sim, ui::Point position)
}
}
void PropertyTool::UpdateConfigurationFromParticle(const Particle &part)
{
auto configuration = GetConfiguration();
switch (configuration->prop.Type)
{
case StructProperty::Float:
{
auto value = *((float*)(((char*)&part)+configuration->prop.Offset));;
configuration->propValue = value;
configuration->propertyValueStr = String::Build(value);
}
break;
case StructProperty::ParticleType:
case StructProperty::Integer:
{
auto value = *((int*)(((char*)&part)+configuration->prop.Offset));;
configuration->propValue = value;
configuration->propertyValueStr = String::Build(value);
}
break;
case StructProperty::UInteger:
{
auto value = *((unsigned int*)(((char*)&part)+configuration->prop.Offset));;
configuration->propValue = value;
configuration->propertyValueStr = String::Build(value);
}
break;
default:
break;
}
SetConfiguration(configuration);
}
void PropertyTool::Draw(Simulation *sim, Brush const &cBrush, ui::Point position)
{
for (ui::Point off : cBrush)

View File

@ -40,11 +40,11 @@ void SampleTool::Draw(Simulation * sim, Brush const &brush, ui::Point position)
}
if (part)
{
auto *propTool = static_cast<PropertyTool *>(gameModel.GetToolFromIdentifier("DEFAULT_UI_PROPERTY"));
if (gameModel.GetActiveTool(0) == propTool && propTool->GetConfiguration())
if (shiftBehaviour)
{
propTool->UpdateConfigurationFromParticle(*part);
gameModel.SetActiveTool(0, propTool); // trigger change so Renderer::findingElement is updated
auto *propTool = static_cast<PropertyTool *>(gameModel.GetToolFromIdentifier("DEFAULT_UI_PROPERTY"));
gameModel.SetActiveTool(0, propTool);
propTool->OpenWindow(gameModel.GetSimulation(), part);
}
else if (part->type == PT_LIFE)
{

View File

@ -25,6 +25,9 @@ public:
RGB<uint8_t> const Colour;
bool const Blocky;
float Strength = 1.0f;
bool shiftBehaviour = false;
bool ctrlBehaviour = false;
bool altBehaviour = false;
Tool(int id, String name, String description,
RGB<uint8_t> colour, ByteString identifier, std::unique_ptr<VideoBuffer> (*textureGen)(int, Vec2<int>) = NULL, bool blocky = false
@ -110,6 +113,9 @@ public:
};
private:
void SetProperty(Simulation *sim, ui::Point position);
void SetConfiguration(std::optional<Configuration> newConfiguration);
GameModel &gameModel;
std::optional<Configuration> configuration;
@ -126,17 +132,13 @@ public:
virtual ~PropertyTool()
{}
void OpenWindow(Simulation *sim);
void SetProperty(Simulation *sim, ui::Point position);
void UpdateConfigurationFromParticle(const Particle &part);
void OpenWindow(Simulation *sim, const Particle *takePropertyFrom);
void Click(Simulation * sim, Brush const &brush, ui::Point position) override { }
void Draw(Simulation *sim, Brush const &brush, ui::Point position) override;
void DrawLine(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2, bool dragging = false) override;
void DrawRect(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2) override;
void DrawFill(Simulation * sim, Brush const &brush, ui::Point position) override;
void SetConfiguration(std::optional<Configuration> newConfiguration);
std::optional<Configuration> GetConfiguration() const
{
return configuration;

View File

@ -16,12 +16,15 @@ ToolButton::ToolButton(ui::Point position, ui::Point size, String text, ByteStri
Component::TextPosition(buttonDisplayText);
}
void ToolButton::OnMouseClick(int x, int y, unsigned int button)
void ToolButton::OnMouseDown(int x, int y, unsigned int button)
{
isButtonDown = true;
if (MouseDownInside)
{
isButtonDown = true;
}
}
void ToolButton::OnMouseUnclick(int x, int y, unsigned int button)
void ToolButton::OnMouseClick(int x, int y, unsigned int button)
{
if(isButtonDown)
{

View File

@ -9,7 +9,7 @@ class ToolButton: public ui::Button
ByteString toolIdentifier;
public:
ToolButton(ui::Point position, ui::Point size, String text, ByteString toolIdentifier, String toolTip = String());
void OnMouseUnclick(int x, int y, unsigned int button) override;
void OnMouseDown(int x, int y, unsigned int button) override;
void OnMouseUp(int x, int y, unsigned int button) override;
void OnMouseClick(int x, int y, unsigned int button) override;
void Draw(const ui::Point& screenPos) override;

View File

@ -51,7 +51,7 @@ void AvatarButton::Draw(const Point& screenPos)
}
}
void AvatarButton::OnMouseUnclick(int x, int y, unsigned int button)
void AvatarButton::OnMouseClick(int x, int y, unsigned int button)
{
if(button != 1)
{
@ -70,16 +70,19 @@ void AvatarButton::OnContextMenuAction(int item)
//Do nothing
}
void AvatarButton::OnMouseClick(int x, int y, unsigned int button)
void AvatarButton::OnMouseDown(int x, int y, unsigned int button)
{
if(button == SDL_BUTTON_RIGHT)
if (MouseDownInside)
{
if(menu)
menu->Show(GetScreenPos() + ui::Point(x, y));
}
else
{
isButtonDown = true;
if(button == SDL_BUTTON_RIGHT)
{
if(menu)
menu->Show(GetContainerPos() + ui::Point(x, y));
}
else
{
isButtonDown = true;
}
}
}

View File

@ -30,7 +30,7 @@ public:
virtual ~AvatarButton() = default;
void OnMouseClick(int x, int y, unsigned int button) override;
void OnMouseUnclick(int x, int y, unsigned int button) override;
void OnMouseDown(int x, int y, unsigned int button) override;
void OnMouseEnter(int x, int y) override;
void OnMouseLeave(int x, int y) override;

View File

@ -86,7 +86,7 @@ void Button::Draw(const Point& screenPos)
if (Enabled)
{
if (isButtonDown || (isTogglable && toggle))
if ((isButtonDown && MouseDownInside) || (isTogglable && toggle))
{
textColour = Appearance.TextActive;
borderColour = Appearance.BorderActive;
@ -140,7 +140,7 @@ void Button::Draw(const Point& screenPos)
}
}
void Button::OnMouseUnclick(int x, int y, unsigned int button)
void Button::OnMouseClick(int x, int y, unsigned int button)
{
if(button == 1)
{
@ -171,17 +171,20 @@ void Button::OnMouseUp(int x, int y, unsigned int button)
isAltButtonDown = false;
}
void Button::OnMouseClick(int x, int y, unsigned int button)
void Button::OnMouseDown(int x, int y, unsigned int button)
{
if(!Enabled)
return;
if(button == 1)
if (MouseDownInside)
{
isButtonDown = true;
}
else if(button == 3)
{
isAltButtonDown = true;
if(!Enabled)
return;
if(button == 1)
{
isButtonDown = true;
}
else if(button == 3)
{
isAltButtonDown = true;
}
}
}

View File

@ -18,7 +18,7 @@ public:
virtual ~Button() = default;
void OnMouseClick(int x, int y, unsigned int button) override;
void OnMouseUnclick(int x, int y, unsigned int button) override;
void OnMouseDown(int x, int y, unsigned int button) override;
void OnMouseUp(int x, int y, unsigned int button) override;
void OnMouseEnter(int x, int y) override;

View File

@ -124,17 +124,21 @@ void Component::SetParent(Panel* new_parent)
this->_parent = new_parent;
}
Point Component::GetScreenPos()
Point Component::GetContainerPos()
{
Point newPos(0,0);
if(GetParentWindow())
newPos += GetParentWindow()->Position;
if(GetParent())
newPos += GetParent()->Position + GetParent()->ViewportPosition;
newPos += Position;
return newPos;
}
Point Component::GetScreenPos()
{
return GetContainerPos() + Position;
}
Graphics * Component::GetGraphics()
{
return parentstate_->GetGraphics();
@ -185,11 +189,7 @@ void Component::OnMouseHover(int localx, int localy)
{
}
void Component::OnMouseMoved(int localx, int localy, int dx, int dy)
{
}
void Component::OnMouseMovedInside(int localx, int localy, int dx, int dy)
void Component::OnMouseMoved(int localx, int localy)
{
}
@ -201,10 +201,6 @@ void Component::OnMouseLeave(int localx, int localy)
{
}
void Component::OnMouseUnclick(int localx, int localy, unsigned button)
{
}
void Component::OnMouseUp(int x, int y, unsigned button)
{
}

View File

@ -43,6 +43,8 @@ namespace ui
bool Enabled;
bool Visible;
bool DoesTextInput;
bool MouseInside;
bool MouseDownInside;
ui::Appearance Appearance;
//virtual void SetAppearance(ui::Appearance);
@ -51,6 +53,7 @@ namespace ui
void Refresh();
Point GetContainerPos();
Point GetScreenPos();
/* See the parent of this component.
@ -94,20 +97,8 @@ namespace ui
// Params:
// localx: Local mouse X position.
// localy: Local mouse Y position.
// dx: Mouse X delta.
// dy: Mouse Y delta.
///
virtual void OnMouseMoved(int localx, int localy, int dx, int dy);
///
// Called: When the mouse moves.
// Params:
// localx: Local mouse X position.
// localy: Local mouse Y position.
// dx: Mouse X delta.
// dy: Mouse Y delta.
///
virtual void OnMouseMovedInside(int localx, int localy, int dx, int dy);
virtual void OnMouseMoved(int localx, int localy);
///
// Called: When the mouse moves on top of the item.
@ -146,7 +137,7 @@ namespace ui
virtual void OnMouseUp(int x, int y, unsigned button);
///
// Called: When a mouse button is pressed on top of the item.
// Called: When a mouse button is pressed and then released on top of the item.
// Params:
// x: X position of the mouse.
// y: Y position of the mouse.
@ -154,15 +145,6 @@ namespace ui
///
virtual void OnMouseClick(int localx, int localy, unsigned button);
///
// Called: When a mouse button is released on top of the item.
// Params:
// x: X position of the mouse.
// y: Y position of the mouse.
// button: The button that is being released.
///
virtual void OnMouseUnclick(int localx, int localy, unsigned button);
///
// Called: When the mouse wheel moves/changes.
// Params:

View File

@ -133,7 +133,7 @@ void DirectionSelector::Draw(const ui::Point& screenPos)
g->BlendEllipse(center + value.offset, { handleRadius, handleRadius }, borderColor);
}
void DirectionSelector::OnMouseMoved(int x, int y, int dx, int dy)
void DirectionSelector::OnMouseMoved(int x, int y)
{
if (mouseDown)
{
@ -142,11 +142,14 @@ void DirectionSelector::OnMouseMoved(int x, int y, int dx, int dy)
CheckHovering(x, y);
}
void DirectionSelector::OnMouseClick(int x, int y, unsigned button)
void DirectionSelector::OnMouseDown(int x, int y, unsigned button)
{
mouseDown = true;
SetPositionAbs({ x, y });
CheckHovering(x, y);
if (MouseDownInside)
{
mouseDown = true;
SetPositionAbs({ x - Position.X, y - Position.Y });
CheckHovering(x - Position.X, y - Position.Y);
}
}
void DirectionSelector::OnMouseUp(int x, int y, unsigned button)

View File

@ -78,8 +78,8 @@ public:
void SetValues(float x, float y);
void Draw(const ui::Point& screenPos) override;
void OnMouseMoved(int x, int y, int dx, int dy) override;
void OnMouseClick(int x, int y, unsigned int button) override;
void OnMouseMoved(int x, int y) override;
void OnMouseDown(int x, int y, unsigned int button) override;
void OnMouseUp(int x, int y, unsigned button) override;
inline void OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) override { altDown = alt; }
inline void OnKeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) override { altDown = alt; }

View File

@ -15,7 +15,8 @@ class DropDownWindow : public ui::Window
public:
DropDownWindow(DropDown * dropDown):
Window(dropDown->GetScreenPos() + ui::Point(-1, -1 - dropDown->optionIndex * 16), ui::Point(dropDown->Size.X+2, 2+dropDown->options.size()*16)),
Window(dropDown->GetScreenPos() + ui::Point(-1, -1 - (dropDown->optionIndex*16 < dropDown->GetScreenPos().Y ? dropDown->optionIndex*16 : 0)),
ui::Point(dropDown->Size.X+2, 2+dropDown->options.size()*16)),
dropDown(dropDown),
appearance(dropDown->Appearance)
{

View File

@ -83,6 +83,8 @@ void Engine::ShowWindow(Window * window)
{
window->Position.Y = (g->Size().Y - window->Size.Y) / 2;
}
window->Size = window->Size.Min(g->Size());
window->Position = window->Position.Clamp(RectBetween<int>({0, 0}, g->Size()));
/*if(window->Position.Y > 0)
{
windowTargetPosition = window->Position;
@ -268,14 +270,14 @@ void Engine::onTextEditing(String text, int start)
}
}
void Engine::onMouseClick(int x, int y, unsigned button)
void Engine::onMouseDown(int x, int y, unsigned button)
{
mouseb_ |= button;
if (state_ && !ignoreEvents)
state_->DoMouseDown(x, y, button);
}
void Engine::onMouseUnclick(int x, int y, unsigned button)
void Engine::onMouseUp(int x, int y, unsigned button)
{
mouseb_ &= ~button;
if (state_ && !ignoreEvents)

View File

@ -30,8 +30,8 @@ namespace ui
void initialMouse(int x, int y);
void onMouseMove(int x, int y);
void onMouseClick(int x, int y, unsigned button);
void onMouseUnclick(int x, int y, unsigned button);
void onMouseDown(int x, int y, unsigned button);
void onMouseUp(int x, int y, unsigned button);
void onMouseWheel(int x, int y, int delta);
void onKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt);
void onKeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt);

View File

@ -88,24 +88,27 @@ void Label::OnContextMenuAction(int item)
}
}
void Label::OnMouseClick(int x, int y, unsigned button)
void Label::OnMouseDown(int x, int y, unsigned button)
{
if(button == SDL_BUTTON_RIGHT)
if (MouseDownInside)
{
if (menu)
if(button == SDL_BUTTON_RIGHT)
{
menu->Show(GetScreenPos() + ui::Point(x, y));
if (menu)
{
menu->Show(GetContainerPos() + ui::Point(x, y));
}
}
}
else
{
selecting = true;
auto tp = textPosition - Vec2{ scrollX, 0 };
selectionIndex0 = textWrapper.Point2Index(x - tp.X, y - tp.Y);
selectionIndexL = selectionIndex0;
selectionIndexH = selectionIndex0;
else
{
selecting = true;
auto tp = textPosition - Vec2{ scrollX, 0 };
selectionIndex0 = textWrapper.Point2Index(x - Position.X - tp.X, y - Position.Y - tp.Y);
selectionIndexL = selectionIndex0;
selectionIndexH = selectionIndex0;
updateSelection();
updateSelection();
}
}
}
@ -143,7 +146,7 @@ void Label::OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bo
}
}
void Label::OnMouseMoved(int localx, int localy, int dx, int dy)
void Label::OnMouseMoved(int localx, int localy)
{
if (selecting)
{

View File

@ -58,9 +58,9 @@ namespace ui
void SetTextColour(Colour textColour) { this->textColour = textColour; }
void OnContextMenuAction(int item) override;
virtual void OnMouseClick(int x, int y, unsigned button) override;
virtual void OnMouseDown(int x, int y, unsigned button) override;
void OnMouseUp(int x, int y, unsigned button) override;
void OnMouseMoved(int localx, int localy, int dx, int dy) override;
void OnMouseMoved(int localx, int localy) override;
void OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) override;
void Draw(const Point& screenPos) override;
void Tick(float dt) override;

View File

@ -10,8 +10,7 @@ using namespace ui;
Panel::Panel(Point position, Point size):
Component(position, size),
InnerSize(size),
ViewportPosition(0, 0),
mouseInside(false)
ViewportPosition(0, 0)
{
}
@ -27,6 +26,8 @@ void Panel::AddChild(Component* c)
{
c->SetParent(this);
c->SetParentWindow(this->GetParentWindow());
c->MouseInside = false;
c->MouseDownInside = false;
}
int Panel::GetChildCount()
@ -108,43 +109,40 @@ void Panel::OnKeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl,
void Panel::OnMouseClick(int localx, int localy, unsigned button)
{
bool childclicked = false;
//check if clicked a child
for(int i = children.size()-1; i >= 0 ; --i)
{
//child must be enabled
if(children[i]->Enabled)
{
//is mouse inside?
if( localx >= children[i]->Position.X + ViewportPosition.X &&
localy >= children[i]->Position.Y + ViewportPosition.Y &&
localx < children[i]->Position.X + ViewportPosition.X + children[i]->Size.X &&
localy < children[i]->Position.Y + ViewportPosition.Y + children[i]->Size.Y )
{
childclicked = true;
GetParentWindow()->FocusComponent(children[i]);
children[i]->OnMouseClick(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y, button);
break;
}
}
}
//if a child wasn't clicked, send click to ourself
if(!childclicked)
{
XOnMouseClick(localx, localy, button);
GetParentWindow()->FocusComponent(this);
}
XOnMouseClick(localx, localy, button);
}
void Panel::OnMouseDown(int x, int y, unsigned button)
{
XOnMouseDown(x, y, button);
for (size_t i = 0; i < children.size(); ++i)
if (MouseDownInside)
{
if(children[i]->Enabled)
children[i]->OnMouseDown(x, y, button);
auto localx = x - Position.X;
auto localy = y - Position.Y;
//check if clicked a child
for(int i = children.size()-1; i >= 0 ; --i)
{
//child must be enabled
if(children[i]->Enabled)
{
//is mouse inside?
if( localx >= children[i]->Position.X + ViewportPosition.X &&
localy >= children[i]->Position.Y + ViewportPosition.Y &&
localx < children[i]->Position.X + ViewportPosition.X + children[i]->Size.X &&
localy < children[i]->Position.Y + ViewportPosition.Y + children[i]->Size.Y )
{
GetParentWindow()->FocusComponent(children[i]);
children[i]->MouseDownInside = true;
break;
}
}
}
XOnMouseDown(x, y, button);
for (size_t i = 0; i < children.size(); ++i)
{
if(children[i]->Enabled)
children[i]->OnMouseDown(x - Position.X - ViewportPosition.X, y - Position.Y - ViewportPosition.Y, button);
}
}
}
@ -155,12 +153,14 @@ void Panel::OnMouseHover(int localx, int localy)
{
if (children[i]->Enabled)
{
if( localx >= children[i]->Position.X &&
localy >= children[i]->Position.Y &&
localx < children[i]->Position.X + children[i]->Size.X &&
localy < children[i]->Position.Y + children[i]->Size.Y )
auto px = children[i]->Position.X + ViewportPosition.X;
auto py = children[i]->Position.Y + ViewportPosition.Y;
if( localx >= px &&
localy >= py &&
localx < px + children[i]->Size.X &&
localy < py + children[i]->Size.Y )
{
children[i]->OnMouseHover(localx - children[i]->Position.X, localy - children[i]->Position.Y);
children[i]->OnMouseHover(localx - px, localy - py);
break;
}
}
@ -170,25 +170,26 @@ void Panel::OnMouseHover(int localx, int localy)
XOnMouseHover(localx, localy);
}
void Panel::OnMouseMoved(int localx, int localy, int dx, int dy)
void Panel::OnMouseMoved(int localx, int localy)
{
XOnMouseMoved(localx, localy, dx, dy);
PropagateMouseMove();
XOnMouseMoved(localx, localy);
for (size_t i = 0; i < children.size(); ++i)
{
if(children[i]->Enabled)
children[i]->OnMouseMoved(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y, dx, dy);
children[i]->OnMouseMoved(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y);
}
}
void Panel::OnMouseMovedInside(int localx, int localy, int dx, int dy)
void Panel::PropagateMouseMove()
{
mouseInside = true;
auto localx = ui::Engine::Ref().GetMouseX() - GetScreenPos().X;
auto localy = ui::Engine::Ref().GetMouseY() - GetScreenPos().Y;
for (size_t i = 0; i < children.size(); ++i)
{
if (children[i]->Enabled)
{
Point local (localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y)
, prevlocal (local.X - dx, local.Y - dy);
Point local (localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y);
// mouse currently inside?
if( local.X >= 0 &&
@ -196,14 +197,12 @@ void Panel::OnMouseMovedInside(int localx, int localy, int dx, int dy)
local.X < children[i]->Size.X &&
local.Y < children[i]->Size.Y )
{
children[i]->OnMouseMovedInside(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y, dx, dy);
children[i]->OnMouseMoved(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y);
// was the mouse outside?
if(!(prevlocal.X >= 0 &&
prevlocal.Y >= 0 &&
prevlocal.X < children[i]->Size.X &&
prevlocal.Y < children[i]->Size.Y ) )
if (!children[i]->MouseInside)
{
children[i]->MouseInside = true;
children[i]->OnMouseEnter(local.X, local.Y);
}
}
@ -211,71 +210,59 @@ void Panel::OnMouseMovedInside(int localx, int localy, int dx, int dy)
else
{
// was the mouse inside?
if( prevlocal.X >= 0 &&
prevlocal.Y >= 0 &&
prevlocal.X < children[i]->Size.X &&
prevlocal.Y < children[i]->Size.Y )
if (children[i]->MouseInside)
{
children[i]->MouseInside = false;
children[i]->OnMouseLeave(local.X, local.Y);
}
}
}
}
// always allow hover on parent (?)
XOnMouseMovedInside(localx, localy, dx, dy);
}
void Panel::OnMouseEnter(int localx, int localy)
{
mouseInside = true;
XOnMouseEnter(localx, localy);
}
void Panel::OnMouseLeave(int localx, int localy)
{
mouseInside = false;
XOnMouseLeave(localx, localy);
}
void Panel::OnMouseUnclick(int localx, int localy, unsigned button)
{
bool childunclicked = false;
//check if clicked a child
for(int i = children.size()-1; i >= 0 ; --i)
{
//child must be unlocked
if(children[i]->Enabled)
{
//is mouse inside?
if( localx >= children[i]->Position.X + ViewportPosition.X &&
localy >= children[i]->Position.Y + ViewportPosition.Y &&
localx < children[i]->Position.X + ViewportPosition.X + children[i]->Size.X &&
localy < children[i]->Position.Y + ViewportPosition.Y + children[i]->Size.Y )
{
childunclicked = true;
children[i]->OnMouseUnclick(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y, button);
break;
}
}
}
//if a child wasn't clicked, send click to ourself
if (!childunclicked)
{
XOnMouseUnclick(localx, localy, button);
}
}
void Panel::OnMouseUp(int x, int y, unsigned button)
{
auto localx = x - Position.X;
auto localy = y - Position.Y;
//check if clicked a child
for(int i = children.size()-1; i >= 0 ; --i)
{
//child must be enabled
if(children[i]->Enabled)
{
//is mouse inside?
if( children[i]->MouseDownInside &&
localx >= children[i]->Position.X + ViewportPosition.X &&
localy >= children[i]->Position.Y + ViewportPosition.Y &&
localx < children[i]->Position.X + ViewportPosition.X + children[i]->Size.X &&
localy < children[i]->Position.Y + ViewportPosition.Y + children[i]->Size.Y )
{
children[i]->OnMouseClick(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y, button);
break;
}
}
}
for (auto *child : children)
{
child->MouseDownInside = false;
}
XOnMouseUp(x, y, button);
for (size_t i = 0; i < children.size(); ++i)
{
if (children[i]->Enabled)
children[i]->OnMouseUp(x, y, button);
children[i]->OnMouseUp(x - Position.X - ViewportPosition.X, y - Position.Y - ViewportPosition.Y, button);
}
}
@ -342,11 +329,7 @@ void Panel::XOnMouseHover(int localx, int localy)
{
}
void Panel::XOnMouseMoved(int localx, int localy, int dx, int dy)
{
}
void Panel::XOnMouseMovedInside(int localx, int localy, int dx, int dy)
void Panel::XOnMouseMoved(int localx, int localy)
{
}
@ -358,10 +341,6 @@ void Panel::XOnMouseLeave(int localx, int localy)
{
}
void Panel::XOnMouseUnclick(int localx, int localy, unsigned button)
{
}
void Panel::XOnMouseUp(int x, int y, unsigned button)
{
}

View File

@ -51,14 +51,12 @@ namespace ui
void Draw(const Point& screenPos) override;
void OnMouseHover(int localx, int localy) override;
void OnMouseMoved(int localx, int localy, int dx, int dy) override;
void OnMouseMovedInside(int localx, int localy, int dx, int dy) override;
void OnMouseMoved(int localx, int localy) override;
void OnMouseEnter(int localx, int localy) override;
void OnMouseLeave(int localx, int localy) override;
void OnMouseDown(int x, int y, unsigned button) override;
void OnMouseUp(int x, int y, unsigned button) override;
void OnMouseClick(int localx, int localy, unsigned button) override;
void OnMouseUnclick(int localx, int localy, unsigned button) override;
void OnMouseWheel(int localx, int localy, int d) override;
void OnMouseWheelInside(int localx, int localy, int d) override;
void OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) override;
@ -67,7 +65,6 @@ namespace ui
protected:
// child components
std::vector<ui::Component*> children;
bool mouseInside;
// Overridable. Called by XComponent::Tick()
virtual void XTick(float dt);
@ -80,10 +77,7 @@ namespace ui
virtual void XOnMouseHover(int localx, int localy);
// Overridable. Called by XComponent::OnMouseMoved()
virtual void XOnMouseMoved(int localx, int localy, int dx, int dy);
// Overridable. Called by XComponent::OnMouseMovedInside()
virtual void XOnMouseMovedInside(int localx, int localy, int dx, int dy);
virtual void XOnMouseMoved(int localx, int localy);
// Overridable. Called by XComponent::OnMouseEnter()
virtual void XOnMouseEnter(int localx, int localy);
@ -100,9 +94,6 @@ namespace ui
// Overridable. Called by XComponent::OnMouseClick()
virtual void XOnMouseClick(int localx, int localy, unsigned button);
// Overridable. Called by XComponent::OnMouseUnclick()
virtual void XOnMouseUnclick(int localx, int localy, unsigned button);
// Overridable. Called by XComponent::OnMouseWheel()
virtual void XOnMouseWheel(int localx, int localy, int d);
@ -114,6 +105,8 @@ namespace ui
// Overridable. Called by XComponent::OnKeyRelease()
virtual void XOnKeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt);
void PropagateMouseMove();
};
}

View File

@ -64,19 +64,22 @@ void RichLabel::SetText(String newText)
regions = newRegions;
}
void RichLabel::OnMouseClick(int x, int y, unsigned button)
void RichLabel::OnMouseDown(int x, int y, unsigned button)
{
int cursorPosition = displayTextWrapper.Point2Index(x - textPosition.X, y - textPosition.Y).raw_index;
for (auto const &region : regions)
if (MouseDownInside)
{
if (region.begin <= cursorPosition && region.end > cursorPosition)
int cursorPosition = displayTextWrapper.Point2Index(x - Position.X - textPosition.X, y - Position.Y - textPosition.Y).raw_index;
for (auto const &region : regions)
{
if (auto *linkAction = std::get_if<RichTextRegion::LinkAction>(&region.action))
if (region.begin <= cursorPosition && region.end > cursorPosition)
{
Platform::OpenURI(linkAction->uri);
return;
if (auto *linkAction = std::get_if<RichTextRegion::LinkAction>(&region.action))
{
Platform::OpenURI(linkAction->uri);
return;
}
}
}
}
Label::OnMouseClick(x, y, button);
Label::OnMouseDown(x, y, button);
}

View File

@ -24,6 +24,6 @@ namespace ui
RichLabel(Point position, Point size, String text);
void SetText(String newText) override;
void OnMouseClick(int x, int y, unsigned button) override;
void OnMouseDown(int x, int y, unsigned button) override;
};
}

View File

@ -262,7 +262,7 @@ void SaveButton::Draw(const Point& screenPos)
}
}
void SaveButton::OnMouseUnclick(int x, int y, unsigned int button)
void SaveButton::OnMouseClick(int x, int y, unsigned int button)
{
if(button != 1)
{
@ -333,36 +333,40 @@ void SaveButton::OnContextMenuAction(int item)
}
}
void SaveButton::OnMouseClick(int x, int y, unsigned int button)
void SaveButton::OnMouseDown(int x, int y, unsigned int button)
{
if(button == SDL_BUTTON_RIGHT)
if (MouseDownInside)
{
if(menu)
menu->Show(GetScreenPos() + ui::Point(x, y));
}
else
{
isButtonDown = true;
if(button !=1 && selectable)
if(button == SDL_BUTTON_RIGHT)
{
selected = !selected;
DoSelection();
if(menu)
menu->Show(GetContainerPos() + ui::Point(x, y));
}
else
{
isButtonDown = true;
if(button !=1 && selectable)
{
selected = !selected;
DoSelection();
}
}
}
}
void SaveButton::OnMouseMovedInside(int x, int y, int dx, int dy)
void SaveButton::OnMouseMoved(int x, int y)
{
if(y > Size.Y-11)
isMouseInsideAuthor = true;
else
isMouseInsideAuthor = false;
isMouseInsideAuthor = false;
isMouseInsideHistory = false;
if (MouseInside)
{
if (y > Size.Y-11)
isMouseInsideAuthor = true;
if(showVotes && y > Size.Y-29 && y < Size.Y - 18 && x > 0 && x < 9)
isMouseInsideHistory = true;
else
isMouseInsideHistory = false;
if (y > Size.Y-29 && y < Size.Y - 18 && x > 0 && x < 9)
isMouseInsideHistory = true;
}
}
void SaveButton::OnMouseEnter(int x, int y)

View File

@ -48,12 +48,12 @@ public:
virtual ~SaveButton();
void OnMouseClick(int x, int y, unsigned int button) override;
void OnMouseUnclick(int x, int y, unsigned int button) override;
void OnMouseDown(int x, int y, unsigned int button) override;
void OnMouseEnter(int x, int y) override;
void OnMouseLeave(int x, int y) override;
void OnMouseMovedInside(int x, int y, int dx, int dy) override;
void OnMouseMoved(int x, int y) override;
void AddContextMenu(int menuType);
void OnContextMenuAction(int item) override;

View File

@ -1,8 +1,9 @@
#include "ScrollPanel.h"
#include "Engine.h"
#include "graphics/Graphics.h"
#include "Misc.h"
#include "PowderToySDL.h"
#include "Window.h"
#include <algorithm>
using namespace ui;
@ -70,28 +71,56 @@ void ScrollPanel::Draw(const Point& screenPos)
}
}
void ScrollPanel::XOnMouseClick(int x, int y, unsigned int button)
void ScrollPanel::XOnMouseDown(int x, int y, unsigned int button)
{
if (isMouseInsideScrollbar)
if (MouseDownInside)
{
scrollbarSelected = true;
scrollbarInitialYOffset = int(offsetY);
CancelPanning();
if (isMouseInsideScrollbar)
{
scrollbarSelected = true;
scrollbarInitialYOffset = int(offsetY);
}
initialOffsetY = offsetY;
scrollbarInitialYClick = y - Position.Y;
scrollbarClickLocation = 100;
}
scrollbarInitialYClick = y;
scrollbarClickLocation = 100;
}
void ScrollPanel::CancelPanning()
{
panning = false;
panHistory = {};
yScrollVel = 0;
}
void ScrollPanel::XOnMouseUp(int x, int y, unsigned int button)
{
scrollbarSelected = false;
auto oldPanHistory = panHistory;
CancelPanning();
{
auto it = oldPanHistory.end();
while (it != oldPanHistory.begin() && *(it - 1))
{
--it;
}
if (it < oldPanHistory.end())
{
auto offsetYDiff = oldPanHistory.back()->offsetY - (*it)->offsetY;
auto tickDiff = oldPanHistory.back()->ticks - (*it)->ticks;
yScrollVel += offsetYDiff / tickDiff * (1000.f / Engine::Ref().GetFps());
}
}
isMouseInsideScrollbarArea = false;
scrollbarClickLocation = 0;
}
void ScrollPanel::XOnMouseMoved(int x, int y, int dx, int dy)
void ScrollPanel::XOnMouseMoved(int x, int y)
{
if(maxOffset.Y>0 && InnerSize.Y>0)
{
auto oldViewportPositionY = ViewportPosition.Y;
float scrollHeight = float(Size.Y)*(float(Size.Y)/float(InnerSize.Y));
float scrollPos = 0;
if (-ViewportPosition.Y>0)
@ -113,6 +142,19 @@ void ScrollPanel::XOnMouseMoved(int x, int y, int dx, int dy)
offsetY = float(scrollbarInitialYOffset);
}
}
else if (MouseDownInside)
{
Vec2<int> mouseAt{ x, y };
if (Engine::Ref().TouchUI && iabs(scrollbarInitialYClick - mouseAt.Y) > PanOffsetThreshold)
{
panning = true;
for (auto *child : children)
{
child->MouseDownInside = false;
}
GetParentWindow()->FocusComponent(NULL);
}
}
if (x > (Size.X-scrollBarWidth) && x < (Size.X-scrollBarWidth)+scrollBarWidth)
{
@ -122,11 +164,31 @@ void ScrollPanel::XOnMouseMoved(int x, int y, int dx, int dy)
}
else
isMouseInsideScrollbar = false;
if (oldViewportPositionY != ViewportPosition.Y)
{
PropagateMouseMove();
}
}
}
void ScrollPanel::XTick(float dt)
{
auto oldViewportPositionY = ViewportPosition.Y;
if (panning)
{
auto scrollY = initialOffsetY + scrollbarInitialYClick - (Engine::Ref().GetMouseY() - GetScreenPos().Y);
ViewportPosition.Y = -scrollY;
offsetY = float(scrollY);
PanPoint p{ offsetY, GetTicks() };
if (!(panHistory.back() && panHistory.back()->ticks == p.ticks))
{
std::copy(panHistory.begin() + 1, panHistory.end(), panHistory.begin());
panHistory.back() = p;
}
}
if (xScrollVel > 7.0f) xScrollVel = 7.0f;
if (xScrollVel < -7.0f) xScrollVel = -7.0f;
if (xScrollVel > -0.5f && xScrollVel < 0.5)
@ -183,9 +245,9 @@ void ScrollPanel::XTick(float dt)
}
}
if (mouseInside && scrollBarWidth < 6)
if (MouseInside && scrollBarWidth < 6)
scrollBarWidth++;
else if (!mouseInside && scrollBarWidth > 0 && !scrollbarSelected)
else if (!MouseInside && scrollBarWidth > 0 && !scrollbarSelected)
scrollBarWidth--;
if (isMouseInsideScrollbarArea && scrollbarClickLocation && !scrollbarSelected)
@ -205,4 +267,9 @@ void ScrollPanel::XTick(float dt)
offsetY += scrollbarClickLocation*scrollHeight/10;
ViewportPosition.Y -= int(scrollbarClickLocation*scrollHeight/10);
}
if (oldViewportPositionY != ViewportPosition.Y)
{
PropagateMouseMove();
}
}

Some files were not shown because too many files have changed in this diff Show More