Compare commits

..

88 commits

Author SHA1 Message Date
Skye
7ab521cba6 libc: add actual sprintf to sprintf.c
Change-Id: Iba19b587781da3191c7674a6a141c0c4fbf8b344
2026-05-12 10:19:51 -04:00
Skye
25551180ba libc: add basic maximum field width support to sscanf
Change-Id: Iba6d8af32435beccf9a77d801b0eaac91549bc89
2026-05-12 10:16:45 -04:00
Christian Soffke
51abd937d5 playlist viewer: retrieve track name id3 from db
Metadata for formatting the trackname can
be read from the database instead of from
disk. I think this was changed by mistake,
when a single function was added for both
Show Track Info and track name formatting.

Change-Id: I903d36bb513898242505f01562340924bf642a12
2026-05-12 09:38:29 -04:00
Christian Soffke
9bda6389ce quickscreen: fix UI update when USB connected
Regression introduced in commit 7aca1d4.

Quickscreen kept redrawing itself over the USB screen,
because remove_event_ex was only called after returning
from it.

Change-Id: I8a187809781cef46d13ed45392efecb28435a9df
2026-05-11 21:58:13 +02:00
Aidan MacDonald
075d1deac3 pcm_mixer: remove some unused preprocessor symbols
Change-Id: I0c9c96f082cd4bb8313ade9a8d8da7fa2ba087e4
2026-05-11 07:19:50 -04:00
Christian Soffke
bc528c4079 plugins: properties: don't clear UI viewport for dirs
Accidentally made the vp flash in previous commit.

Dir scanning doesn't display a progress bar
and already handles its own drawing

Change-Id: Id0e67d62081dfe4b22e91c775cd80af2e55a4b69
2026-05-09 07:03:58 +02:00
Christian Soffke
325a028af4 plugins: properties: clear UI viewport at startup
Otherwise, with themes that adjust the viewport
for the current activity, the background behind
the progress bar may look glitchy while
scanning items, until the list is displayed

Change-Id: I27a207b37c3209eae9bc61e1fd0862fb5872be57
2026-05-08 17:00:15 +02:00
Christian Soffke
ae871d25a9 gui: skin_engine: reduce updates
Slight optimization of c145d19. Not supposed to result in
any visible difference.

- if UI viewport is drawn for GUI_EVENT_NEED_UI_UPDATE,
  it doesn't need to do a viewport update; skin_render
  already updates the display

- skin_render_deferred shouldn't need to request that
  the skin perform an immediate update

Change-Id: Id03cf89357eaf0d61af1e928c94942d8c4882dba
2026-05-08 15:43:55 +02:00
Christian Soffke
ce403586e0 playlist_viewer: show loading splash for current playlist after delay
Provide UI feedback if playlist can't be displayed in time.

Change-Id: I5faabc2690aaeb6989f342dc284e1e7e38ba1d7d
2026-05-08 04:36:18 +02:00
Christian Soffke
05f1a6605d gui: skin_engine: fix dirty & force_waiting ignoring multiple screens
In part regression introduced in c145d19e85.

force_waiting not taking multiple screens into
account appears to be pre-existing issue.

Change-Id: Iabfc2933470145eb512c8f2763fb350e170cb1fa
2026-05-08 04:36:18 +02:00
Christian Soffke
20194cb606 gui: wps: render SBS and WPS in one batch
For themes that display the SBS on the WPS,
update display only once, instead of separately.

Change-Id: I773207ef2ddbe185eb287505950ba8322624d380
2026-05-07 22:28:55 -04:00
JJ Style
34054eaa42 dart_scorer: scale better on smaller devices
Calculates sizes of strings to position properly on device, and reduce length of some strings so it scales better on smaller devices as it was unusable on a sansa clip zip

Change-Id: Iad16c15cf85cb79fc9a9ee7146aa40c1c741c26d
2026-05-06 13:20:48 -04:00
Christian Soffke
7aca1d46b8 quickscreen: fix possible flickering for GUI_EVENT_NEED_UI_UPDATE
Immediately redraw when skin engine does refresh, so
themes that draw over UI viewport don't cause visible
flickering

Change-Id: I6f314cdfbd1136c710b9fee7526673e2f8b98849
2026-05-06 18:25:11 +02:00
Skye
e37111c3eb erosq: make volume buttons work in quickscreen
Change-Id: If0efdf1cb05bf665815e772bcc7063654e8f21ac
2026-05-06 08:24:16 -04:00
Skye
1d5aa53321 playback: do not try to switch to a sampr the current sink doesn't support
Change-Id: I73c18365cb3010ca45c6d41ae390b8de095c7589
2026-05-06 08:22:30 -04:00
mojyack
d8a4730159 tools: fix reggen is not cleaned
Change-Id: Iacb3f7b2c3104435a3188c9b24eb8f4374ef8b59
2026-05-04 20:33:52 -04:00
mojyack
142e1864ef usb: fix get_max_packet_size is called before endpoints are allocated
retrive the requirements like others rather than callback

Change-Id: I20efce76a418ebd7aa6943f02e53b3f7a8fd2797
2026-05-04 16:46:57 -04:00
mojyack
8873cbb57e usb: designware: support max packet size override
Change-Id: I75d3aca1599ce064c604c96f2b5ed4c041f975b8
2026-05-04 16:45:57 -04:00
Solomon Peachy
d92b42c70f Fix Yellow & Red in 41caf678fe
Change-Id: If111595271289878097c2a8b30bec3f18390a49c
2026-05-04 14:04:26 -04:00
mojyack
41caf678fe usb: allow more flexible endpoint allocation
the capabilities of endpoint of several devices such as dwc2 change during
runtime, so they cannot be determined during driver initialization.
therefore, allocation using ep_specs is inappropriate.

to support these devices, add functions to the driver that determine whether
endpoints are available and make allocation more flexible.

tested with ipodvideo(arc) and erosqnative(designware)

Change-Id: I8005c17f3d763cd17306bf49918e1cd8084bdeff
2026-05-04 13:31:21 -04:00
Christian Soffke
42841d493f gui: inbuilt statusbar: defer viewport update
Don't independently update the statusbar
viewport when rendering the skin

Change-Id: Idb5a983c5b08fe60db7ab239d6dfbc60190768fb
2026-05-04 13:16:16 -04:00
Christian Soffke
c145d19e85 gui: align display updates, reduce UI glitches
Based on commits ce33902 and 8990d52 (without PictureFlow)
from "Rockpod" fork by Nux Li (https://github.com/nuxcodes/rockpod),
with some adjustments.

Addresses flickering when:
- plugin is opened/closed
- activity changes
- theme is toggled
- QuickScreen is opened

In these cases, skin_render will not immediately update the display
anymore, but instead will wait until the UI viewport is ready to be
drawn as well, so we don't produce unnecessary visual glitches.

Change-Id: I8bed8f06221d3e767a32450f199e69d742bc61cd
2026-05-04 13:16:09 -04:00
Christian Soffke
89d24f3bd4 list: fix GUI_EVENT_THEME_CHANGED timing issue
Initialize a list's dirty_tick to last_dirty_tick instead of to the
current tick.

Issue probably only affects the sim:

To force a list to reinitialize using the GUI_EVENT_THEME_CHANGED
event, last_dirty_tick is set to the current tick.

list_is_dirty() checks whether the list viewport needs to be
re-initialized by comparing the list's dirty tick to last_dirty_tick,
and seeing if time has passed.

In some scenarios though, the list's vp may be initialized, become
immediately dirty, and list_is_dirty is called, all in the same tick.

Change-Id: Ia379117a07bbaf545e0a16d35e74888955893441
2026-05-04 13:58:59 +02:00
Vencislav Atanasov
02638c1cb8 s5l87xx: (Re)name the SHA-1 registers
Synced with the s5l8702-sha1 Linux driver in the freemyipod fork

Change-Id: I5243346205f7883f4271d4b272936dd125adb496
2026-05-04 07:16:29 -04:00
mojyack
bbdada7690 usb: enable cpu boost for specific class drivers
add needs_cpu_boost field to usb_class_driver and manage boost state in
usb_core, similar to needs_exclusive_storage.

Change-Id: Ieb0cd7bedda5b24bb0d209d5ce907db30f4815db
2026-05-04 18:15:52 +09:00
mojyack
732f7bcfd9 usb: allow class drivers to return error from init_connection()
Change-Id: Idcd48dd00d9d2f06c49a347e02a41a5de7252431
2026-05-04 18:15:49 +09:00
mojyack
3c10b21c10 usb: hold class_driver instance in each driver
Change-Id: Ia2f857bffcc6c3cca4dee59791c48c628082595b
2026-05-03 15:18:51 -04:00
Solomon Peachy
82a0921399 build: Fix yellow in 6d387e2e7f when panic is not supported
Change-Id: I8b896ef05ca27b3abc05d5d6910d5f6b9741047c
2026-05-03 15:10:03 -04:00
mojyack
6d387e2e7f usb: allow class drivers to override max packet size
this is required to make hid endpoint of iap class driver work,
especially on ipodvideo(arc).
at least for arc, it is required to set mps as 64 instead of 512 on
highspeed, or some accessories ignore incoming hid reports.

Change-Id: I242060faced28a66204146a9c36ef10626d6d265
2026-05-03 14:59:15 -04:00
mojyack
fad99773e3 send iap status change notifications
install iap event notification callbacks in various locations

Change-Id: I637a3ad18cb07ca056ad9b678400ba11d2f8faad
2026-05-03 14:20:29 -04:00
mojyack
e50ad40814 usb: increase usb thread stack size
Change-Id: I07283a68056e095efba8019dac2aa37d65c0ef6c
2026-05-03 14:15:56 -04:00
mojyack
c2e1094383 playback: reserve an aa slot for iap
Change-Id: I605017148b6f3c62021e63f58a1ddd8e229c5fbb
2026-05-03 13:04:49 -04:00
mojyack
757e683506 usb: core: handle apple vendor usb request
Change-Id: Iab5135774353630e7bce4939f40ca35940e214f3
2026-05-03 13:03:42 -04:00
mojyack
3bb656625b usb: add usb iAP driver
add class driver source files.
also register iap audio sink.
usbstack/iap/libiap directory is imported from libiap.

Change-Id: I776c5caec33fe9efadc448e2e3b37d500bf19c9f
2026-05-03 12:40:54 -04:00
mojyack
2b9e4a8d70 config: define USB_ENABLE_IAP
iap is only enabled for idevices.
it will not work without apple vendor id anyway.

Change-Id: I1696dbc8a2304fc5eecc5432b4c52e25801c468e
2026-05-03 12:40:54 -04:00
Christian Soffke
6d699f08f4 plugins: imageviewer: fix incomplete previous commits
missed in commit 2690418:
grayscale targets need to have access
to the Display Options menu now, since
it contains the hide_info option

missed in commit f4dc4d8:
"resizing" message for bmp files wasn't
hidden even with hide_info enabled

Change-Id: I1a73e3816305ab6f032fc226d79f09df0d9aa96b
2026-05-03 18:09:44 +02:00
mojyack
ae8013405a plugins: rockboy: respect GLOBAL_LDOPTS
Change-Id: I5224e39b41bda71506ee99d9df79b7b19f7e29cb
2026-05-03 09:06:29 -04:00
mojyack
790a8aa560 plugins: chessbox: respect GLOBAL_LDOPTS
Change-Id: Ia9b20c24f91a7fd9fa7b0a73e352199aeaf6dd05
2026-05-03 09:06:17 -04:00
Aidan MacDonald
2053bba561 makefiles: fix duke3d overlay not using objcopy_plugin
This should use $(call objcopy_plugin) in order to
generate the correct binary format on platforms where
the plugin binary format differs from the main binary
format (ie. STM32).

Change-Id: I027cc74e3c5d55b9a3538f4f16c3fd5ece25a4b5
2026-05-03 13:10:08 +01:00
Aidan MacDonald
30adfbf5c9 puzzles: fix possible crash due to non-NULL terminated list
Change-Id: Id0cadfd6d2d88e8cd27a34d042403ec54e8aca41
2026-05-03 13:10:08 +01:00
Solomon Peachy
d839cab05a manual: Correct a minor error in the countdown timer documentation
Change-Id: I5105a2ebf51e030c9d58a234c521b473141e71f7
2026-05-03 08:08:43 -04:00
Solomon Peachy
30ad7f4ee8 manual: Mention [lack of] unicode normalization in the manual
Change-Id: Icdb19a47367fbe7b2fd32679885a26669a799612
2026-05-03 08:08:43 -04:00
Aidan MacDonald
4f2c918f28 mpegplayer: disable ARM assembly code for Cortex-M
Change-Id: I6fe8a359ef6d6160918239bf5c45a3034e52356b
2026-05-03 12:19:48 +01:00
Solomon Peachy
81fcb10f8f metadata: Normalize all metadata to Unicode NFC form
* Standalone database tool
 * Simulator builds
 * Target firmware (Hosted and Native, for all >2MB targets)

Change-Id: Ia7361affc2fc6a08e73c31ecc9ef3a4008c2415d
2026-05-03 07:07:07 -04:00
Solomon Peachy
8768266d27 Simpler const fix for fill_metadata_from_path()
Make sure the output of strchr(const*) is assinged to a const*

This way the filename argument can stay const

Change-Id: Ie46be936491eb62ba2a7e729b8cd7881e205bba7
2026-05-02 23:01:21 -04:00
Solomon Peachy
026ce110f2 Revert "fill_metadata_from_path modifies a "const" argument"
This reverts commit 3eba3ecd17.
2026-05-02 22:58:45 -04:00
Solomon Peachy
3eba3ecd17 fill_metadata_from_path modifies a "const" argument
...so convert the argument to non-const

Change-Id: I9e04d817e8674facae7c3be234fd9756f1a4f8ad
2026-05-02 20:41:28 -04:00
Aidan MacDonald
bafc796ce7 Fix errors from stdint.h removal (cf6fb81346)
Change-Id: Ie9457121448f47db14300e035dc5b5eccd086884
2026-05-02 23:22:03 +01:00
Aidan MacDonald
cf6fb81346 firmware: drop hand-rolled <stdint.h> from libc
Perhaps this was needed in the distant past, but these days
GCC provides a working <stdint.h> for freestanding targets.

Change-Id: I4f02f12058a13b6a086ccc52f02a12ce21a986c1
2026-05-02 22:33:12 +01:00
mojyack
472acce401 reggen.h: fix broken stm32 build with gcc-14
Change-Id: Ib504e6a75ac88be325adc6dd19c88bf263178f2d
2026-05-02 17:28:45 -04:00
Solomon Peachy
0dc670d25c build: Make bitmap object files position independent to silence linker warnings
Finally addresses the "warning: creating DT_TEXTREL in a shared object" warnings
seen when linking plugins that reference bitmaps.

This currently only happens with simulator builds using recent-ish
toolchains (GCC >= 12 IIRC).  However, binutils 2.46 promotes this
warning to an error, so it's finally tracked down and addressed.

Change-Id: I4b4926c14f7c0047496892c55009c26da2a4756d
2026-05-02 13:22:51 -04:00
Christian Soffke
88d4903d10 gui: fix "lock screens" making UI viewport disappear
Themes like Adwaitapod, Themify, or FreshOS have
custom "lock screens" that are drawn on top of the
UI viewport.

Request a full skin update when unlocked, so you don't
have to press a button to make the hidden UI viewport
appear again.

Change-Id: Idf5023b4e12f7aea1cd7a2e9d9ab2f754387dc48
2026-05-02 16:53:02 +02:00
Christian Soffke
83e55164f4 gui: remove SBS lock/unlock redraw lag
Stop the lock indicators on the SBS from lagging
behind their actual state when lock notifications
are disabled.

Request immediate skin update in button loop, so
the device doesn't feel laggy.

Change-Id: I42955f65d9ad4ca9196549d806538d1badb5f79d
2026-05-02 16:52:03 +02:00
Solomon Peachy
89dd08a3b4 configure: Explicitly disallow erosqnative_v3/v4 for "normal" builds
They are only intended for bootloaders.

Change-Id: I4da3e7acd55b803e016bc9c42526860dfdc6fa9a
2026-05-02 07:47:36 -04:00
Solomon Peachy
f886bfc572 misc: Address issues uncovered with GCC 16 + binutils 2.46 (1/N)
* Funky macro-based definitions for memchr and strstr
   which require an #undef before we use our own in codecs & plugins
 * Return value of of strstr is const

Still have several more warnings and link failure with some plugins
but this is a good start.

Change-Id: Ife1f2d3e6f0e0629e3125a9058abc39c6102f452
2026-05-01 22:50:14 -04:00
Solomon Peachy
8e8206f6d5 FS#13886 - Updated Serbian translation (Ivan Pešić)
Change-Id: I10321a41db686ebbfdadf82c84d3797551fe7319
2026-05-01 11:41:39 -04:00
William Wilgus
52edc2e069 [Feature] allow displaying the WPS/tree hotkey menu with hotkey press
add 'Context Menu' item to WPS and Tree hotkeys

this allows a user to display a menu of hotkey actions to execute
when they press the hotkey

items are voiced

added 'View Album Art'

Change-Id: I2199c4de536f347016e7a8d7f3c063da0b56a9a0
2026-05-01 11:07:49 -04:00
Solomon Peachy
4ea2f57aaa lang: Update URL for translation site
Change-Id: I0e054a2bbae994e2eadd81abf096d4ac463d4bb5
2026-05-01 10:18:25 -04:00
Christian Soffke
dbcee0deae gui: defer deadspace viewport update
Change-Id: Idac4a2bf21ced25cbf4349dc32ef62b3a456f999
2026-04-29 15:44:53 -04:00
Christian Soffke
c41beebcda gui: delay updating SBS when setting list title
We currently force a skin refresh when setting the
list title. This causes very noticeable flickering
of the list, if the SBS draws over the UI viewport,
when there is no displayable list content yet
(For an example, check out the Adwaitapod theme).

Instead, only mark the title as dirty. Later, when
drawing the list, register for a UI update callback
and ask the skin engine to render, so that we can
draw the list at the same time.

Note: Flickering related to display updates when
switching activities or when toggling the theme is
unrelated to this, and will still need to be addressed
in separate commits.

Change-Id: Icce899905aa311deccb0cc498aacce2866aaae8a
2026-04-29 15:44:30 -04:00
Christian Soffke
7ab1a81806 simple_viewer: use UI viewport and SBS title
Also adjust scrollbar margins and height so it matches
the look of normal lists, and hide scrollbars when set
to SCROLLBAR_OFF.

Change-Id: I27f6de7b16cf5ec72e12c7d6377a8772d84947ac
2026-04-29 14:19:48 -04:00
William Wilgus
2d419b4b93 [Feature] FS#13850 - Add SELECT button support in calculator on FiiO M3K
baremetal port add select as a second way to enter

Change-Id: I0e3ab76fee8e6accd4425def3da48fb12a6142e8
2026-04-29 11:29:10 -04:00
William Wilgus
121c65b32a [Bugfix] FS#13857 - Keylock with USB bug (Fiio M3K)
1. Lock buttons
2. Lock indicator turns on
3. Plug USB
4. Lock indicator still turned on
5. Unplug USB
6. Lock indicator turns off but buttons still locked and if you
press lock button you see "Buttons locked" splash

pretty sure this is an issue with the touchpad on this device
probably applies to other touchscreen targets too

Change-Id: Ia0afee7d737f3a5a2755f53d176bd53dd57d87c5
2026-04-29 10:30:54 -04:00
William Wilgus
1068433d5b [Bugfix] FS#13878 FS#13862 lua alpha bmp overflow and image flip example
FS#13878 - 292x216 images cause panic
 alpha channel causes overflow

FS#13862 - In rlimg example "flip image" cause error
 missing local variable

save random and rainbow images

fix ball bounce direction

Change-Id: I717eb029f30bf63d2eef0b7997eb04036ffeda15
2026-04-29 02:17:46 -04:00
William Wilgus
6928581bf9 [bugfix] open_plugin_import fails to import full path WIP
settings_load_config() only reads up to 128 characters
while openplugin entries could be upwards of 600

instead use the open_plugins plugin to restore entries

add import from .cfg file
add save to .cfg file
better dupe checking

Change-Id: Iec2506aad14a3eb89dcc558b0fbc1f014aad98b5
2026-04-26 12:56:13 -04:00
Christian Soffke
6cf705886d skin: custom scrollbar: fix OBOE
Change-Id: I0eb7463c39970c78321d53a51a61f463858dac4e
2026-04-26 11:20:06 -04:00
Christian Soffke
5bbf1c8e5b tree: fix gui_synclist_scroll_stop called with uninitialized list
Regression introduced in 7a281ec.

Fixes crash with the "remember last folder" setting, when the
saved dir has since been removed:

exit_to_new_screen in dirbrowse calls gui_synclist_scroll_stop
when update_dir returns ≤0 for the number of dir entries.
But synclist has not been initialized by update_dir when it
returns early with -1 due to either tagtree_load or ft_load failing.

Move gui_synclist init further up in update_dir, so that when it
returns, the list is guaranteed to be initialized.

Change-Id: I62aa742a3d0121d5034440ff134992034e13fd90
2026-04-26 11:19:47 -04:00
Solomon Peachy
792a230c00 FS#13877 - Use FONT_UI in the Equalizer and scale sliders to match font size
The graphical EQ was configured to use SYSFONT, which is limited to
ASCII despite the EQ being translateable.  Change this to use
the current UI font instead.

As the UI font can vary in size quite drastically, alter the EQ slider
from a fixed 6px height (intended for use with an 8px SYSFONT) to 3/4 of
the height of the selected font.

Change-Id: I05e7e77be37e9b8cf633b31c12bc4ef02cbaa90a
2026-04-26 08:10:41 -04:00
Solomon Peachy
83214cf18c FS#13883 - Latvian translation update (Renalds Belaks)
Change-Id: Iaa60196c9316bb90eb228b3812561dd5f81b1034
2026-04-26 07:33:46 -04:00
Aidan MacDonald
31d8118581 plugins: sdl: fix LoadWAVStream failing to load WAV files
Reading the WAV length seems to have been accidentally
commented out in commit e28d1fe916. The WAV length is
not used here but disabling the read broke WAV header
parsing completely.

Change-Id: Ia6d0b1a168b2b029bd1cbec9bdc482caf6fa0487
2026-04-25 15:54:34 +01:00
Aidan MacDonald
80e3c0b065 mips: require 8-byte stack alignment
The o32 ABI requires at least 8-byte alignment. This fixes
the float formatting weirdness seen in quake (FS#13821).

Change-Id: I4b587946884d7b35cef420e607c7e127664849e2
2026-04-25 12:36:46 +01:00
Christian Soffke
2690418551 plugins: imageviewer: use theme in all submenus
Affects submenus "Toggle Slideshow Mode"
and "Slideshow Time" that were left un-themed.

Also prevents theme from being re-toggled
when entering "Display Options" submenu.

Change-Id: I3995d5eb12bbc8fb868c179db8043576eb675dbc
2026-04-24 15:25:41 -04:00
mojyack
6dc731dff2 include pcm.h in pcm_mixer.h for pcm_peaks
Change-Id: I074fff95b5147a343d23e22e876b33884a173c97
2026-04-24 20:07:29 +09:00
Aidan MacDonald
d02ad9b749 pcm: improve workaround for false -Warray-bounds in pcm_switch_sink
Change-Id: I81ff414ed07bbc61367250c25dc99c11e79d21d2
2026-04-23 20:43:07 -04:00
Christian Soffke
7960dbb9a7 plugins: Fix muted mixer channel (FS#13809)
Fixes regression where you may not have heard
any audio from some of the plugins modified by
commit 017dd72, due to the playback channel not
being unmuted.

Change-Id: Iaa184161c79d353dff6ef9bf3e0b39778c8b1bcd
2026-04-24 01:01:14 +02:00
Christian Soffke
d1abd788d0 pcm_mixer: fix missing NULL check for play_cbs
Fixes Metronome plugin crashing
(regression introduced in cb04b81)

Does *not* fix missing Metronome sound
(regression introduced in 017dd72)

Change-Id: I1da9aa2c937b267a3d5b122c431eaa9f7e748440
2026-04-23 15:13:17 +02:00
Aidan MacDonald
f9a5d6fe86 quake: fix crash from strange printf behavior (FS#13821)
This was a latent bug exposed by commit a3f2b64a46
("Enable float formatting in printf"). The version
number is passed as 1.09f, but somehow ends up printing
a string that is so huge it overruns the destination
buffer and corrupts the return address on the stack.

Using snprintf prevents the buffer overrun and gets
quake working again, though this doesn't address the
underlying bug with printf.

Change-Id: I37e4426bc6ebca42d83b5a3b659da497b652d1ae
2026-04-22 22:49:36 +01:00
Aidan MacDonald
e661c35b29 dircache: increase stack size to avoid stack overflows
On FS#13821, a test build with -fstack-protector-all
(which adds 4 bytes of stack to each function, roughly)
overflowed the dircache stack on the Hifiwalker H2.
In my own testing, the dircache thread hit 94% stack
use on the Fiio M3K using the same settings.

That seems a bit too close to the limit, especially
since the dircache uses recursion and might consume
more stack space for deeply nested directories.

Adding 768 bytes should provide enough of a safety
margin. This increases the stack size from 1.25k to
2k on most targets, including all X1000 targets.

Change-Id: I900c19da9fb33f539d02b00830aedeb15c7449e2
2026-04-22 22:07:28 +01:00
Aidan MacDonald
d815053360 Add support for -fstack-protector in native builds
-fstack-protector only needs a small amount of runtime
support to work on native builds. It increases code size
by ~1.5% on ARM/MIPS; -fstack-protector-strong adds 3-4%.
This is disabled by default and must be enabled by passing
'--with-stack-protector' to configure.

Change-Id: If952e711d3673c9b469895f08c7bff70b3d95df6
2026-04-21 21:16:28 -04:00
Aidan MacDonald
9ac6edf750 Add panicf to plugin and codec API
Change-Id: I0e11ecaf8e18233f682f572f479cfdd141c99bd5
2026-04-21 19:04:25 -04:00
Solomon Peachy
4e62c9f8cb FS#13879 - German Translation update (Karl Huber)
Change-Id: Icee3d2778b4a0afaf567776e43a996a8e99d2cce
2026-04-21 12:55:36 -04:00
Christian Soffke
088b3345fb pitchscreen: Fix unbalanced pcmbuf_set_low_latency when connecting USB
Change-Id: I73652ef27c24485faefe16c678406d78b0d6e0fd
2026-04-19 17:57:41 -04:00
Christian Soffke
9000614224 pitchscreen: Fix dropouts when button held down
Change-Id: I0f23ad6463f7938e832b0c5da2c121c5a5e5faa8
2026-04-19 17:57:34 -04:00
Vencislav Atanasov
1adadc943d Change which macro is used to check for Windows targets
This fixes a false-positive on macOS that was caused by the macro "#define TARGET_OS_WIN32 0"

Change-Id: I1fcfb19b5aae4f63f00b9500094d619c4f7eea4b
2026-04-18 13:43:49 -04:00
Solomon Peachy
9dce0c3258 Work around a false positive compiler warning in pcm_switch_sink()
CC firmware/pcm.c
firmware/pcm.c: In function ‘pcm_switch_sink’:
firmware/pcm.c:311:38: warning: array subscript 1 is above array bounds of ‘struct pcm_sink *[1]’ [-Warray-bounds]
  311 |     struct pcm_sink* old_sink = sinks[cur_sink];
      |                                 ~~~~~^~~~~~~~~~
firmware/pcm.c:79:25: note: while referencing ‘sinks’
   79 | static struct pcm_sink* sinks[PCM_SINK_NUM] = {
      |

PCM_SINK_NUM is 1, and cur_sink is initialized to 0.  It can never be set
above 0.  cur_sink can never be >= PCM_SINK_NUM, ie 0, but for some reason
the compiler thinks otherwise.... sometimes.

This only shows up on native ARM builds with GCC9.5.0

Change-Id: I1aa731a4ee21c46a264c8b70833e3b43e777e8a7
2026-04-17 21:56:38 -04:00
Solomon Peachy
39abe4f698 Translation updates:
* Simplified Chinese (Wang Ji)
 * Italian (Alessio Lenzi)

Change-Id: I21dcbfe07b128d080a10e52d2d4ebedd592e6868
2026-04-17 20:56:29 -04:00
mojyack
9ffc8a00ce pcm_mixer: implement mixer_switch_sink
Change-Id: I1549470774f96a6f470817cbc5fe4611812de6fa
2026-04-17 20:54:10 -04:00
mojyack
be4b0591ee pcm: implement pcm_switch_sink
Change-Id: Iace01c2e97950cc794f3cf755dc358da6f3daa7f
2026-04-17 20:52:23 -04:00
331 changed files with 12702 additions and 1301 deletions

View file

@ -28,6 +28,7 @@
#if !defined(BOOTLOADER)
#include "language.h"
#include "skin_engine/skin_engine.h"
#endif
#include "appevents.h"
@ -937,19 +938,20 @@ static inline void do_softlock(action_last_t *last, action_cur_t *cur)
if (notify_user)
{
#ifndef BOOTLOADER
skin_request_update_locked(last->keys_locked);
#endif
action_handle_backlight(true, false);
#ifdef HAVE_BACKLIGHT
/* If we don't wait for a moment for the backlight queue to process,
* the user will never see the message
*/
if (!is_backlight_on(false))
{
sleep(HZ/2);
}
#endif
if (!has_flag(last->softlock_mask, SEL_ACTION_ALLNONOTIFY))
{
#ifdef HAVE_BACKLIGHT
/* If we don't wait for a moment for the backlight queue to process,
* the user will never see the message
*/
if (!is_backlight_on(false))
sleep(HZ/2);
#endif
if (last->keys_locked)
{
splash(HZ/2, ID2P(LANG_KEYLOCK_ON));
@ -1440,6 +1442,9 @@ bool is_keys_locked(void)
void set_selective_softlock_actions(bool selective, unsigned int mask)
{
action_last.keys_locked = false;
#if defined(HAVE_TOUCHPAD) || defined(HAVE_TOUCHSCREEN)
button_enable_touch(true);
#endif
if (selective)
{
action_last.softlock_mask = mask | SEL_ACTION_ENABLED;

View file

@ -50,3 +50,11 @@ $(BUILDDIR)/apps/bitmaps/remote_mono/%.c: $(ROOTDIR)/apps/bitmaps/remote_mono/%.
$(BUILDDIR)/apps/bitmaps/remote_native/%.c: $(ROOTDIR)/apps/bitmaps/remote_native/%.bmp $(TOOLSDIR)/bmp2rb
$(SILENT)mkdir -p $(dir $@) $(BMPINCDIR)
$(call PRINTS,BMP2RB $(<F))$(BMP2RB_REMOTENATIVE) -b -h $(BMPINCDIR) $< > $@
ifdef APP_TYPE
# Bitmaps must be explicitly Position independent to avoid linker warnings
$(BUILDDIR)/apps/bitmaps/native/%.o: CFLAGS += -fPIC
$(BUILDDIR)/apps/bitmaps/mono/%.o: CFLAGS += -fPIC
$(BUILDDIR)/apps/bitmaps/remote_mono/%.o: CFLAGS += -fPIC
$(BUILDDIR)/apps/bitmaps/remote_native/%.o: CFLAGS += -fPIC
endif

View file

@ -43,6 +43,7 @@
#include "file.h"
#include "pathfuncs.h"
#include "playlist_menu.h"
#include "iap-usb.h"
/*#define LOGF_ENABLE*/
#include "logf.h"
@ -1125,7 +1126,9 @@ static bool play_bookmark(const char* bookmark)
if (parse_bookmark(fnamebuf, sizeof(fnamebuf), bookmark, &resume_info, true))
{
global_settings.repeat_mode = resume_info.repeat_mode;
iap_on_repeat_state(global_settings.repeat_mode);
global_settings.playlist_shuffle = resume_info.shuffle;
iap_on_shuffle_state(global_settings.playlist_shuffle);
#if defined(HAVE_PITCHCONTROL)
sound_set_pitch(resume_info.pitch);
dsp_set_timestretch(resume_info.speed);

View file

@ -50,6 +50,7 @@
#include "splash.h"
#include "general.h"
#include "rbpaths.h"
#include "panic.h"
#define LOGF_ENABLE
#include "logf.h"
@ -150,6 +151,7 @@ struct codec_api ci = {
/* new stuff at the end, sort into place next time
the API gets incompatible */
panicf,
};

View file

@ -46,7 +46,8 @@ static bool search_for_cuesheet(const char *path, struct cuesheet_file *cue_file
{
size_t len;
char cuepath[MAX_PATH];
char *dot, *slash, *slash_cuepath;
char *dot, *slash_cuepath;
const char *slash;
cue_file->pos = 0;
cue_file->size = 0;
@ -281,7 +282,7 @@ bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue)
}
s = skip_whitespace(line);
/* RECOGNIZED TAGS ***********************
/* RECOGNIZED TAGS ***********************
* eCS_TRACK = 0, eCS_INDEX_01, eCS_TITLE,
* eCS_PERFORMER, eCS_SONGWRITER, eCS_FILE,
*/
@ -306,7 +307,7 @@ bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue)
cue->tracks[cue->track_count-1].offset = parse_cue_index(s);
#endif
}
else if (option != eCS_NOTFOUND)
else if (option != eCS_NOTFOUND)
{
char *dest = NULL;
char *string = get_string(s);

View file

@ -316,3 +316,7 @@ lto
#if defined(USB_ENABLE_AUDIO)
usbdac
#endif
#if defined(UTF8PROC_EXPORTS)
utf8proc
#endif

View file

@ -525,7 +525,7 @@ static void read_config_init(int fd)
static int file_find_extension(const char* file)
{
char *extension = strrchr(file, '.');
const char *extension = strrchr(file, '.');
if (extension)
extension++;
return find_extension(extension);
@ -704,7 +704,7 @@ int filetype_load_plugin(const char* plugin, const char* file)
{
int i;
char plugin_name[MAX_PATH];
char *s;
const char *s;
for (i=1;i<filetype_count;i++)
{

View file

@ -163,7 +163,7 @@ void skinlist_get_scrollbar(int* nb_item, int* first_shown, int* last_shown)
{
*nb_item = current_item;
*first_shown = 0;
*last_shown = current_nbitems;
*last_shown = current_nbitems - 1;
}
}
@ -281,8 +281,7 @@ bool skinlist_draw(struct screen *display, struct gui_synclist *list)
}
current_column = -1;
current_row = -1;
display->set_viewport(parent);
display->update_viewport();
skin_render_deferred(display, parent);
current_drawing_line = list->selected_item;
return true;
}

View file

@ -446,8 +446,7 @@ void list_draw(struct screen *display, struct gui_synclist *list)
callback_draw_item(&list_info);
}
display->set_viewport(parent);
display->update_viewport();
skin_render_deferred(display, parent);
display->set_viewport(last_vp);
}

View file

@ -49,6 +49,8 @@
void list_draw(struct screen *display, struct gui_synclist *list);
static long last_dirty_tick;
static bool sb_title_is_dirty;
static bool theme_enabled;
static struct viewport parent[NB_SCREENS];
static struct gui_synclist *current_lists;
@ -57,17 +59,17 @@ static bool list_is_dirty(struct gui_synclist *list)
return TIME_BEFORE(list->dirty_tick, last_dirty_tick);
}
static void list_force_reinit(unsigned short id, void *param, void *last_dirty_tick)
static void list_force_reinit(unsigned short id, void *param)
{
(void)id;
(void)param;
*(int *)last_dirty_tick = current_tick;
last_dirty_tick = current_tick;
}
void list_init(void)
{
last_dirty_tick = current_tick;
add_event_ex(GUI_EVENT_THEME_CHANGED, false, list_force_reinit, &last_dirty_tick);
add_event(GUI_EVENT_THEME_CHANGED, list_force_reinit);
}
static void list_init_viewports(struct gui_synclist *list)
@ -81,7 +83,7 @@ static void list_init_viewports(struct gui_synclist *list)
gui_synclist_set_viewport_defaults(list->parent[i], i);
}
}
list->dirty_tick = current_tick;
list->dirty_tick = last_dirty_tick;
}
static int list_nb_lines(struct gui_synclist *list, enum screen_type screen)
@ -185,7 +187,6 @@ void gui_synclist_init(struct gui_synclist * gui_list,
gui_list->title_icon = Icon_NOICON;
gui_list->scheduled_talk_tick = gui_list->last_talked_tick = 0;
gui_list->dirty_tick = current_tick;
#ifdef HAVE_LCD_COLOR
gui_list->title_color = -1;
@ -220,11 +221,32 @@ int gui_list_get_item_offset(struct gui_synclist * gui_list,
return item_offset;
}
static void sb_title_cb(unsigned short id, void *data, void *userdata)
{
(void)id;
(void)data;
theme_enabled = true;
gui_synclist_draw((struct gui_synclist *) userdata);
}
/*
* Force a full screen update.
*/
void gui_synclist_draw(struct gui_synclist *gui_list)
{
if (sb_title_is_dirty)
{
sb_title_is_dirty = theme_enabled = false;
/* tell skin engine to refresh, then call us back */
add_event_ex(GUI_EVENT_NEED_UI_UPDATE, true, sb_title_cb, gui_list);
send_event(GUI_EVENT_ACTIONUPDATE, (void*)1);
remove_event_ex(GUI_EVENT_NEED_UI_UPDATE, sb_title_cb, gui_list);
/* sb_title_cb was only called if theme is enabled */
if (theme_enabled)
return;
}
if (list_is_dirty(gui_list))
{
list_init_viewports(gui_list);
@ -439,7 +461,7 @@ void gui_synclist_set_title(struct gui_synclist * gui_list,
gui_list->title_icon = icon;
FOR_NB_SCREENS(i)
sb_set_title_text(title, icon, i);
send_event(GUI_EVENT_ACTIONUPDATE, (void*)1);
sb_title_is_dirty = true;
}
void gui_synclist_set_nb_items(struct gui_synclist * lists, int nb_items)

View file

@ -50,33 +50,25 @@
#define MARGIN 10
#define CENTER_ICONAREA_SIZE (MARGIN+8*2)
struct gui_quickscreen
struct quickscreen
{
const struct settings_list *items[QUICKSCREEN_ITEM_COUNT];
struct viewport parent[NB_SCREENS];
struct viewport vps[NB_SCREENS][QUICKSCREEN_ITEM_COUNT];
struct viewport vp_icons[NB_SCREENS];
int button_enter;
enum quickscreen_return result;
};
static bool redraw;
static void quickscreen_update_callback(unsigned short id,
void *data, void *userdata)
{
(void)id;
(void)data;
(void)userdata;
redraw = true;
}
static void quickscreen_fix_viewports(struct gui_quickscreen *qs,
struct screen *display,
struct viewport *parent,
struct viewport
vps[QUICKSCREEN_ITEM_COUNT],
struct viewport *vp_icons)
static void quickscreen_fix_viewports(struct quickscreen *qs, enum screen_type screen)
{
int char_height, width, pad = 0;
int left_width = 0, right_width = 0, vert_lines;
unsigned char *s;
struct screen *display = &screens[screen];
struct viewport *parent = &qs->parent[screen];
struct viewport *vps = qs->vps[screen];
struct viewport *vp_icons = &qs->vp_icons[screen];
int nb_lines = viewport_get_nb_lines(parent);
/* nb_lines only returns the number of fully visible lines, small screens
@ -181,16 +173,15 @@ static void quickscreen_fix_viewports(struct gui_quickscreen *qs,
vps[QUICKSCREEN_RIGHT].flags |= VP_FLAG_ALIGN_RIGHT;
}
static void gui_quickscreen_draw(const struct gui_quickscreen *qs,
struct screen *display,
struct viewport *parent,
struct viewport vps[QUICKSCREEN_ITEM_COUNT],
struct viewport *vp_icons)
static void quickscreen_draw(struct quickscreen *qs, enum screen_type screen)
{
int i;
int temp, i;
char buf[MAX_PATH];
unsigned const char *title, *value;
int temp;
struct screen *display = &screens[screen];
struct viewport *parent = &qs->parent[screen];
struct viewport *vps = qs->vps[screen];
struct viewport *vp_icons = &qs->vp_icons[screen];
struct viewport *last_vp = display->set_viewport(parent);
display->clear_viewport();
@ -204,12 +195,12 @@ static void gui_quickscreen_draw(const struct gui_quickscreen *qs,
title = P2STR(ID2P(qs->items[i]->lang_id));
temp = option_value_as_int(qs->items[i]);
value = option_get_valuestring(qs->items[i],
buf, MAX_PATH, temp);
buf, sizeof buf, temp);
if (viewport_get_nb_lines(vp) < 2)
{
char text[MAX_PATH];
snprintf(text, MAX_PATH, "%s: %s", title, value);
snprintf(text, sizeof text, "%s: %s", title, value);
display->puts_scroll(0, 0, text);
}
else
@ -242,11 +233,19 @@ static void gui_quickscreen_draw(const struct gui_quickscreen *qs,
(vp_icons->width/2) - 4, vp_icons->height - 8, 7, 8);
}
display->set_viewport(parent);
display->update_viewport();
skin_render_deferred(display, parent);
display->set_viewport(last_vp);
}
static void quickscreen_draw_cb(unsigned short id, void *data, void *userdata)
{
(void)id;
(void)data;
FOR_NB_SCREENS(i)
quickscreen_draw((struct quickscreen *) userdata, i);
}
static void talk_qs_option(const struct settings_list *opt, bool enqueue)
{
if (!global_settings.talk_menu || !opt)
@ -263,7 +262,7 @@ static void talk_qs_option(const struct settings_list *opt, bool enqueue)
* - button : the key we are going to analyse
* returns : true if the button corresponded to an action, false otherwise
*/
static bool gui_quickscreen_do_button(struct gui_quickscreen * qs, int button)
static bool quickscreen_do_button(struct quickscreen * qs, int button)
{
int item;
bool previous = false;
@ -343,13 +342,27 @@ static int quickscreen_touchscreen_button(void)
}
#endif
static int gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_enter, bool *usb)
static void cleanup(void *parameter)
{
struct quickscreen *qs = (struct quickscreen *) parameter;
remove_event_ex(GUI_EVENT_NEED_UI_UPDATE, quickscreen_draw_cb, qs);
FOR_NB_SCREENS(i)
{
for (int j = 0; j < QUICKSCREEN_ITEM_COUNT; j++)
screens[i].scroll_stop_viewport(&qs->vps[i][j]);
viewportmanager_theme_undo(i, !(qs->result & QUICKSCREEN_GOTO_SHORTCUTS_MENU));
}
/* Eliminate flashing of parent during transition to Shortcuts */
if (qs->result & QUICKSCREEN_GOTO_SHORTCUTS_MENU)
pop_current_activity_without_refresh();
else
pop_current_activity();
}
static void quickscreen_run(struct quickscreen * qs)
{
int button;
struct viewport parent[NB_SCREENS];
struct viewport vps[NB_SCREENS][QUICKSCREEN_ITEM_COUNT];
struct viewport vp_icons[NB_SCREENS];
int ret = QUICKSCREEN_OK;
/* To quit we need either :
* - a second press on the button that made us enter
* - an action taken while pressing the enter button,
@ -358,17 +371,14 @@ static int gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_enter
push_current_activity(ACTIVITY_QUICKSCREEN);
add_event_ex(GUI_EVENT_NEED_UI_UPDATE, false, quickscreen_update_callback, NULL);
FOR_NB_SCREENS(i)
{
screens[i].set_viewport(NULL);
screens[i].scroll_stop();
viewportmanager_theme_enable(i, true, &parent[i]);
quickscreen_fix_viewports(qs, &screens[i], &parent[i], vps[i], &vp_icons[i]);
gui_quickscreen_draw(qs, &screens[i], &parent[i], vps[i], &vp_icons[i]);
viewportmanager_theme_enable(i, true, &qs->parent[i]);
quickscreen_fix_viewports(qs, i);
quickscreen_draw(qs, i);
}
*usb = false;
/* Announce current selection on entering this screen. This is all
queued up, but can be interrupted as soon as a setting is
changed. */
@ -383,31 +393,28 @@ static int gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_enter
#ifdef HAVE_TOUCHSCREEN
action_gesture_reset();
#endif
while (true) {
if (redraw)
{
redraw = false;
FOR_NB_SCREENS(i)
gui_quickscreen_draw(qs, &screens[i], &parent[i],
vps[i], &vp_icons[i]);
}
add_event_ex(GUI_EVENT_NEED_UI_UPDATE, false, quickscreen_draw_cb, qs);
while (true)
{
button = get_action(CONTEXT_QUICKSCREEN, HZ/5);
#ifdef HAVE_TOUCHSCREEN
if (button == ACTION_TOUCHSCREEN)
button = quickscreen_touchscreen_button();
#endif
if (default_event_handler(button) == SYS_USB_CONNECTED)
if (default_event_handler_ex(button, cleanup, qs)
== SYS_USB_CONNECTED)
{
*usb = true;
break;
qs->result |= QUICKSCREEN_IN_USB;
return;
}
if (gui_quickscreen_do_button(qs, button))
if (quickscreen_do_button(qs, button))
{
ret |= QUICKSCREEN_CHANGED;
qs->result |= QUICKSCREEN_CHANGED;
can_quit = true;
redraw = true;
FOR_NB_SCREENS(i)
quickscreen_draw(qs, i);
}
else if (button == button_enter)
else if (button == qs->button_enter)
can_quit = true;
else if (button == ACTION_QS_VOLUP) {
adjust_volume(1);
@ -421,10 +428,10 @@ static int gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_enter
}
else if (button == ACTION_STD_CONTEXT)
{
ret |= QUICKSCREEN_GOTO_SHORTCUTS_MENU;
qs->result |= QUICKSCREEN_GOTO_SHORTCUTS_MENU;
break;
}
if ((button == button_enter) && can_quit)
if ((button == qs->button_enter) && can_quit)
break;
if (button == ACTION_STD_CANCEL)
@ -432,27 +439,14 @@ static int gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_enter
}
/* Notify that we're exiting this screen */
cond_talk_ids_fq(VOICE_OK);
FOR_NB_SCREENS(i)
{ /* stop scrolling before exiting */
for (int j = 0; j < QUICKSCREEN_ITEM_COUNT; j++)
screens[i].scroll_stop_viewport(&vps[i][j]);
viewportmanager_theme_undo(i, !(ret & QUICKSCREEN_GOTO_SHORTCUTS_MENU));
}
if (ret & QUICKSCREEN_GOTO_SHORTCUTS_MENU) /* Eliminate flashing of parent during */
pop_current_activity_without_refresh(); /* transition to Shortcuts */
else
pop_current_activity();
remove_event_ex(GUI_EVENT_NEED_UI_UPDATE, quickscreen_update_callback, NULL);
return ret;
cleanup(qs);
}
int quick_screen_quick(int button_enter)
{
struct gui_quickscreen qs;
bool usb = false;
struct quickscreen qs;
qs.button_enter = button_enter;
qs.result = QUICKSCREEN_OK;
for (int i = 0; i < 4; ++i)
{
@ -462,13 +456,12 @@ int quick_screen_quick(int button_enter)
qs.items[i] = NULL;
}
int ret = gui_syncquickscreen_run(&qs, button_enter, &usb);
if (ret & QUICKSCREEN_CHANGED)
quickscreen_run(&qs);
if (qs.result & QUICKSCREEN_CHANGED)
settings_save();
if (usb)
return QUICKSCREEN_IN_USB;
return ret & QUICKSCREEN_GOTO_SHORTCUTS_MENU ? QUICKSCREEN_GOTO_SHORTCUTS_MENU :
QUICKSCREEN_OK;
return qs.result & ~QUICKSCREEN_CHANGED;
}
/* stuff to make the quickscreen configurable */

View file

@ -180,7 +180,7 @@ void settings_apply_skins(void)
audio_stop();
bool first_run = skin_backdrop_init();
if (!first_run)
{
/* Make sure all skins unloaded */
@ -342,6 +342,23 @@ void skin_request_full_update(enum skinnable_screens skin)
skins[skin][i].needs_full_update = true;
}
/* Request skin update for lock state change */
void skin_request_update_locked(bool locked)
{
if (get_current_activity() == ACTIVITY_WPS)
return;
sb_skin_force_next_update();
/* fix themes that draw on top of the UI viewport when locked */
if (!locked)
skin_request_full_update(CUSTOM_STATUSBAR);
#ifdef HAS_BUTTON_HOLD
button_queue_post(BUTTON_NONE, 0);
#endif
}
bool dbg_skin_engine(void)
{
struct simplelist_info info;

View file

@ -51,6 +51,11 @@ void skin_disarm_touchregions(struct gui_wps *gwps);
void skin_update(enum skinnable_screens skin, enum screen_type screen,
unsigned int update_type);
/* Defer updates in skin_render */
void skin_defer_rendering(bool deferred);
/* Render viewport together with deferred updates */
void skin_render_deferred(struct screen *display, struct viewport *vp);
bool skin_has_sbs(struct gui_wps *gwps);
@ -82,6 +87,7 @@ void skin_unload_all(void);
bool skin_do_full_update(enum skinnable_screens skin, enum screen_type screen);
void skin_request_full_update(enum skinnable_screens skin);
void skin_request_update_locked(bool locked);
bool dbg_skin_engine(void);

View file

@ -2676,7 +2676,7 @@ bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
if (isfile)
{
/* get the bitmap dir */
char *dot = strrchr(buf, '.');
const char *dot = strrchr(buf, '.');
strmemccpy(bmpdir, buf, dot - buf + 1);
}
else

View file

@ -84,6 +84,8 @@ static void skin_render_playlistviewer(struct playlistviewer* viewer,
unsigned long refresh_type);
static char* skin_buffer;
static bool defer_rendering;
static bool dirty[NB_SCREENS];
static inline struct skin_element*
get_child(OFFSETTYPE(struct skin_element**) children, int child)
@ -295,7 +297,7 @@ static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info,
{
struct skin_albumart *aa = SKINOFFSETTOPTR(skin_buffer, data->albumart);
if (aa)
{
{
int handle = playback_current_aa_hid(data->playback_aa_slot);
#if CONFIG_TUNER
if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF))
@ -841,6 +843,29 @@ void skin_render_viewport(struct skin_element* viewport, struct gui_wps *gwps,
wps_display_images(gwps, &skin_viewport->vp);
}
void skin_defer_rendering(bool deferred)
{
defer_rendering = deferred;
}
void skin_render_deferred(struct screen *display, struct viewport *vp)
{
if (defer_rendering)
return;
if (dirty[display->screen_type])
{
dirty[display->screen_type] = false;
display->set_viewport(NULL);
display->update();
}
else
{
display->set_viewport(vp);
display->update_viewport();
}
}
void skin_render(struct gui_wps *gwps, unsigned refresh_mode)
{
const int vp_is_appearing = (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE);
@ -932,15 +957,19 @@ void skin_render(struct gui_wps *gwps, unsigned refresh_mode)
skin_backdrop_show(data->backdrop_id);
#endif
dirty[display->screen_type] = defer_rendering;
if (((refresh_mode&SKIN_REFRESH_ALL) == SKIN_REFRESH_ALL))
{
defer_rendering = true;
/* If this is the UI viewport then let the UI know
* to redraw itself */
send_event(GUI_EVENT_NEED_UI_UPDATE, NULL);
defer_rendering = dirty[display->screen_type];
}
/* Restore the default viewport */
display->set_viewport_ex(NULL, VP_FLAG_VP_SET_CLEAN);
display->update();
if (!defer_rendering)
display->update();
}
static __attribute__((noinline))

View file

@ -34,6 +34,7 @@
#include "playlist.h"
#include "dsp_misc.h"
#include "playback.h"
#include "iap-usb.h"
/** Disarms all touchregions. */
void skin_disarm_touchregions(struct gui_wps *gwps)
@ -344,6 +345,7 @@ int skin_get_touchaction(struct gui_wps *gwps, int* edge_offset)
case ACTION_TOUCH_SHUFFLE:
global_settings.playlist_shuffle = !global_settings.playlist_shuffle;
iap_on_shuffle_state(global_settings.playlist_shuffle);
replaygain_update();
if (global_settings.playlist_shuffle)
playlist_randomise(NULL, current_tick, true);
@ -358,6 +360,7 @@ int skin_get_touchaction(struct gui_wps *gwps, int* edge_offset)
const struct settings_list *rep_setting =
find_setting(&global_settings.repeat_mode);
option_select_next_val(rep_setting, false, true);
iap_on_repeat_state(global_settings.repeat_mode);
audio_flush_and_reload_tracks();
action = ACTION_REDRAW;
} break;

View file

@ -182,17 +182,18 @@ int sb_get_backdrop(enum screen_type screen)
return -1;
}
#endif
static bool force_waiting = false;
static bool force_waiting[NB_SCREENS];
void sb_skin_update(enum screen_type screen, bool force)
{
struct wps_data *data = skin_get_gwps(CUSTOM_STATUSBAR, screen)->data;
static long next_update[NB_SCREENS] = {0};
static long next_update[NB_SCREENS];
int i = screen;
if (!data->wps_loaded)
return;
if (TIME_AFTER(current_tick, next_update[i]) || force || force_waiting)
if (TIME_AFTER(current_tick, next_update[i]) || force ||
force_waiting[i])
{
force_waiting = false;
force_waiting[i] = false;
#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
/* currently, all remotes are readable without backlight
* so still update those */
@ -214,7 +215,8 @@ void do_sbs_update_callback(unsigned short id, void *param)
/* the WPS handles changing the actual id3 data in the id3 pointers
* we imported, we just want a full update */
skin_request_full_update(CUSTOM_STATUSBAR);
force_waiting = true;
FOR_NB_SCREENS(i)
force_waiting[i] = true;
/* force timeout in wps main loop, so that the update is instantly */
button_queue_post(BUTTON_NONE, 0);
}
@ -224,6 +226,12 @@ void sb_skin_set_update_delay(int delay)
update_delay = delay;
}
void sb_skin_force_next_update(void)
{
FOR_NB_SCREENS(i)
force_waiting[i] = true;
}
/* This creates and loads a ".sbs" based on the user settings for:
* - regular statusbar
* - colours

View file

@ -38,6 +38,7 @@ struct viewport *sb_skin_get_info_vp(enum screen_type screen);
void sb_skin_update(enum screen_type screen, bool force);
void sb_skin_set_update_delay(int delay);
void sb_skin_force_next_update(void);
bool sb_set_title_text(const char* title, enum themable_icons icon, enum screen_type screen);
bool sb_set_persistent_title(const char* title, enum themable_icons icon,
enum screen_type screen);

View file

@ -331,7 +331,6 @@ void gui_statusbar_draw(struct gui_statusbar * bar, bool force_redraw, struct vi
}
#endif
display->setfont(FONT_UI);
display->update_viewport();
display->set_viewport(last_vp);
bar->lastinfo = bar->info;
}

View file

@ -91,13 +91,12 @@ static void toggle_events(bool enable)
#endif
}
static void set_clear_update_valid_vp(enum screen_type screen, struct viewport *vp)
static void set_clear_valid_vp(enum screen_type screen, struct viewport *vp)
{
if (vp->width && vp->height)
{
screens[screen].set_viewport(vp);
screens[screen].clear_viewport();
screens[screen].update_viewport();
}
}
@ -135,27 +134,31 @@ static void toggle_theme(enum screen_type screen, bool force)
deadspace.y = 0;
deadspace.width = screens[screen].lcdwidth;
deadspace.height = user.y;
set_clear_update_valid_vp(screen, &deadspace);
set_clear_valid_vp(screen, &deadspace);
/* below */
deadspace.y = user.y + user.height;
deadspace.height = screens[screen].lcdheight - deadspace.y;
set_clear_update_valid_vp(screen, &deadspace);
set_clear_valid_vp(screen, &deadspace);
/* left */
deadspace.x = 0;
deadspace.y = 0;
deadspace.width = user.x;
deadspace.height = screens[screen].lcdheight;
set_clear_update_valid_vp(screen, &deadspace);
set_clear_valid_vp(screen, &deadspace);
/* below */
deadspace.x = user.x + user.width;
deadspace.width = screens[screen].lcdwidth - deadspace.x;
set_clear_update_valid_vp(screen, &deadspace);
set_clear_valid_vp(screen, &deadspace);
screens[screen].set_viewport(last_vp);
}
intptr_t force = first_boot?0:1;
skin_defer_rendering(true);
send_event(GUI_EVENT_ACTIONUPDATE, (void*)force);
skin_defer_rendering(false);
if (!first_boot)
sb_skin_force_next_update();
}
else
{

View file

@ -533,7 +533,11 @@ static void gwps_leave_wps(bool theme_enabled)
viewports drawn by the WPS. May need further thought... */
struct wps_data *sbs = skin_get_gwps(CUSTOM_STATUSBAR, i)->data;
if (gwps->data->use_extra_framebuffer && sbs->use_extra_framebuffer)
{
skin_defer_rendering(true);
skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL);
skin_defer_rendering(false);
}
#endif
viewportmanager_theme_undo(i, skin_has_sbs(gwps));
}
@ -595,7 +599,10 @@ static void gwps_enter_wps(bool theme_enabled)
skin_backdrop_show(gwps->data->backdrop_id);
#endif
display->clear_display();
if (skin_has_sbs(gwps))
skin_defer_rendering(true);
skin_update(WPS, i, SKIN_REFRESH_ALL);
skin_defer_rendering(false);
}
#ifdef HAVE_TOUCHSCREEN

View file

@ -129,6 +129,10 @@ static const struct button_mapping button_context_quickscreen[] = {
{ ACTION_QS_LEFT, BUTTON_SCROLL_BACK, BUTTON_NONE },
{ ACTION_QS_DOWN, BUTTON_NEXT|BUTTON_REL, BUTTON_NONE },
{ ACTION_QS_DOWN, BUTTON_NEXT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_QS_VOLUP, BUTTON_VOL_UP, BUTTON_NONE },
{ ACTION_QS_VOLUP, BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_QS_VOLDOWN, BUTTON_VOL_DOWN, BUTTON_NONE },
{ ACTION_QS_VOLDOWN, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE },
{ ACTION_STD_CONTEXT,BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU },

View file

@ -14676,7 +14676,7 @@
user: core
<source>
*: "Press LEFT to cancel."
android,hifietma*,zenvision: "Press BACK to cancel."
android,hifietma*: "Press BACK to cancel."
cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Press POWER to cancel."
ihifi760,ihifi960: "Double tap RETURN to cancel."
ihifi770,ihifi770c,ihifi800: "Press HOME to cancel."
@ -14691,7 +14691,7 @@
</source>
<dest>
*: "请按LEFT键取消。"
android,hifietma*,zenvision: "请按BACK键取消。"
android,hifietma*: "请按BACK键取消。"
cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "请按POWER键取消。"
ihifi760,ihifi960: "双击RETURN键取消。"
ihifi770,ihifi770c,ihifi800: "请按HOME键取消。"
@ -14707,7 +14707,7 @@
</dest>
<voice>
*: "请按LEFT键取消"
android,hifietma*,zenvision: "请按BACK键取消"
android,hifietma*: "请按BACK键取消"
cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "请按POWER键取消"
ihifi760,ihifi960: "双击RETURN键取消"
ihifi770,ihifi770c,ihifi800: "请按HOME键取消"
@ -15202,7 +15202,7 @@
</phrase>
<phrase>
id: LANG_VOICED_DATE_FORMAT
desc: format string for how dates will be read back. Y == 4-digit year, A == month name, m == numeric month, d == numeric day. For example, "AdY" will read "January 21 2021"
desc: format string for how dates will be read back. Y == 4-digit year (grouped), y == 4-digit year (numeric), A == month name, m == numeric month, d == numeric day. For example, for 2021-01-05, "AdY" will be voiced as "January 5 twenty twenty-one" and "dmy" will be voiced as "5 1 two thousand twenty one
user: core
<source>
*: "dAY"
@ -15811,16 +15811,16 @@
</phrase>
<phrase>
id: LANG_DEFAULT_BROWSER
desc: in Settings
desc: deprecated
user: core
<source>
*: "Default Browser"
*: ""
</source>
<dest>
*: "默认浏览器"
*: ""
</dest>
<voice>
*: "默认浏览器"
*: ""
</voice>
</phrase>
<phrase>
@ -16967,3 +16967,143 @@
*: "~U S B"
</voice>
</phrase>
<phrase>
id: LANG_ANNOUNCE_STATUS
desc: announnnce_status plugin
user: core
<source>
*: "Announce Status"
</source>
<dest>
*: "播报状态"
</dest>
<voice>
*: "播报状态"
</voice>
</phrase>
<phrase>
id: LANG_KEEP_DIRECTORY
desc: file browser setting
user: core
<source>
*: "Always remember last folder"
</source>
<dest>
*: "总是记住上次的文件夹"
</dest>
<voice>
*: "总是记住上次的文件夹"
</voice>
</phrase>
<phrase>
id: LANG_FILE_NOT_FOUND
desc: When file does not exist
user: core
<source>
*: "File not found"
</source>
<dest>
*: "找不到文件"
</dest>
<voice>
*: "找不到文件"
</voice>
</phrase>
<phrase>
id: LANG_SHOW_IN_FILES
desc: Reveal item in File Browser
user: core
<source>
*: "Show in Files"
</source>
<dest>
*: "按文件展示"
</dest>
<voice>
*: "按文件展示"
</voice>
</phrase>
<phrase>
id: LANG_CHANNEL_SWAP
desc: in sound_settings
user: core
<source>
*: "Swap Left & Right"
</source>
<dest>
*: "切换左&右"
</dest>
<voice>
*: "切换左右"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_SET
desc: countdown_timer plugin - header shown on the setup screen where the user enters the countdown duration
user: core
<source>
*: "SET TIMER"
</source>
<dest>
*: "设定计时器"
</dest>
<voice>
*: "设定计时器"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_RUNNING
desc: countdown_timer plugin - status label shown while the countdown is active
user: core
<source>
*: "RUNNING"
</source>
<dest>
*: "运行中"
</dest>
<voice>
*: "运行中"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_PAUSED
desc: countdown_timer plugin - status label shown while the countdown is paused
user: core
<source>
*: "PAUSED"
</source>
<dest>
*: "暂停"
</dest>
<voice>
*: "暂停"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_OVERTIME
desc: countdown_timer plugin - status label shown when the countdown has passed zero and is counting up
user: core
<source>
*: "OVERTIME"
</source>
<dest>
*: "超时"
</dest>
<voice>
*: "超时"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_FINISHED
desc: countdown_timer plugin - status label shown at the moment the countdown expires
user: core
<source>
*: "FINISHED"
</source>
<dest>
*: "完成"
</dest>
<voice>
*: "完成"
</voice>
</phrase>

View file

@ -15822,16 +15822,16 @@
</phrase>
<phrase>
id: LANG_DEFAULT_BROWSER
desc: in Settings
desc: deprecated
user: core
<source>
*: "Default Browser"
*: ""
</source>
<dest>
*: "Standardbrowser"
*: ""
</dest>
<voice>
*: "Standardbrowser"
*: ""
</voice>
</phrase>
<phrase>
@ -17006,3 +17006,115 @@
*: "~U S B"
</voice>
</phrase>
<phrase>
id: LANG_FILE_NOT_FOUND
desc: When file does not exist
user: core
<source>
*: "File not found"
</source>
<dest>
*: "Datei nicht gefunden"
</dest>
<voice>
*: "Datei nicht gefunden"
</voice>
</phrase>
<phrase>
id: LANG_SHOW_IN_FILES
desc: Reveal item in File Browser
user: core
<source>
*: "Show in Files"
</source>
<dest>
*: "Zeige in Dateien"
</dest>
<voice>
*: "Zeige in Dateien"
</voice>
</phrase>
<phrase>
id: LANG_CHANNEL_SWAP
desc: in sound_settings
user: core
<source>
*: "Swap Left & Right"
</source>
<dest>
*: "Rechts & Links tauschen"
</dest>
<voice>
*: "Rechts und Links tauschen"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_SET
desc: countdown_timer plugin - header shown on the setup screen where the user enters the countdown duration
user: core
<source>
*: "SET TIMER"
</source>
<dest>
*: "Timer stellen"
</dest>
<voice>
*: "Timer stellen"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_RUNNING
desc: countdown_timer plugin - status label shown while the countdown is active
user: core
<source>
*: "RUNNING"
</source>
<dest>
*: "LÄUFT"
</dest>
<voice>
*: "Läuft"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_PAUSED
desc: countdown_timer plugin - status label shown while the countdown is paused
user: core
<source>
*: "PAUSED"
</source>
<dest>
*: "ANGEHALTEN"
</dest>
<voice>
*: "Angehalten"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_OVERTIME
desc: countdown_timer plugin - status label shown when the countdown has passed zero and is counting up
user: core
<source>
*: "OVERTIME"
</source>
<dest>
*: "ÜBERZEIT"
</dest>
<voice>
*: "Überzeit"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_FINISHED
desc: countdown_timer plugin - status label shown at the moment the countdown expires
user: core
<source>
*: "FINISHED"
</source>
<dest>
*: "BEENDET"
</dest>
<voice>
*: "Beendet"
</voice>
</phrase>

View file

@ -78,7 +78,7 @@
# tools/voice-corrections.txt for further details.
#
# To validate the contents of a translation, you can use the Rockbox
# translation site at https://translate.rockbox.org. Alternatively, you
# translation site at https://www.rockbox.org/translate/ Alternatively, you
# can use the command-line 'updatelang' tool as follows:
#
# tools/updatelang apps/lang/english.lang path/to/translation.lang path/to/updated.lang

View file

@ -15804,16 +15804,16 @@
</phrase>
<phrase>
id: LANG_DEFAULT_BROWSER
desc: in Settings
desc: deprecated
user: core
<source>
*: "Default Browser"
*: ""
</source>
<dest>
*: "Browser Predefinito"
*: ""
</dest>
<voice>
*: "Browser Predefinito"
*: ""
</voice>
</phrase>
<phrase>
@ -16988,3 +16988,115 @@
*: "~U S B"
</voice>
</phrase>
<phrase>
id: LANG_FILE_NOT_FOUND
desc: When file does not exist
user: core
<source>
*: "File not found"
</source>
<dest>
*: "File non trovato"
</dest>
<voice>
*: "File non trovato"
</voice>
</phrase>
<phrase>
id: LANG_SHOW_IN_FILES
desc: Reveal item in File Browser
user: core
<source>
*: "Show in Files"
</source>
<dest>
*: "Mostra nei file"
</dest>
<voice>
*: "Mostra nei file"
</voice>
</phrase>
<phrase>
id: LANG_CHANNEL_SWAP
desc: in sound_settings
user: core
<source>
*: "Swap Left & Right"
</source>
<dest>
*: "Scambia sinistro & destro"
</dest>
<voice>
*: "Scambia sinistro e destro"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_SET
desc: countdown_timer plugin - header shown on the setup screen where the user enters the countdown duration
user: core
<source>
*: "SET TIMER"
</source>
<dest>
*: "IMPOSTA TIMER"
</dest>
<voice>
*: "Imposta timer"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_RUNNING
desc: countdown_timer plugin - status label shown while the countdown is active
user: core
<source>
*: "RUNNING"
</source>
<dest>
*: "IN CORSO"
</dest>
<voice>
*: "In corso"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_PAUSED
desc: countdown_timer plugin - status label shown while the countdown is paused
user: core
<source>
*: "PAUSED"
</source>
<dest>
*: "IN PAUSA"
</dest>
<voice>
*: "In pausa"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_OVERTIME
desc: countdown_timer plugin - status label shown when the countdown has passed zero and is counting up
user: core
<source>
*: "OVERTIME"
</source>
<dest>
*: "TEMPO SUPPLEMENTARE"
</dest>
<voice>
*: "Tempo supplementare"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_FINISHED
desc: countdown_timer plugin - status label shown at the moment the countdown expires
user: core
<source>
*: "FINISHED"
</source>
<dest>
*: "TERMINATO"
</dest>
<voice>
*: "Terminato"
</voice>
</phrase>

View file

@ -14781,7 +14781,7 @@
user: core
<source>
*: "Press LEFT to cancel."
android,hifietma*,zenvision: "Press BACK to cancel."
android,hifietma*: "Press BACK to cancel."
cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Press POWER to cancel."
ihifi760,ihifi960: "Double tap RETURN to cancel."
ihifi770,ihifi770c,ihifi800: "Press HOME to cancel."
@ -14796,7 +14796,7 @@
</source>
<dest>
*: "Nospiediet LEFT, lai atceltu."
android,hifietma*,zenvision: "Nospiediet BACK, lai atceltu."
android,hifietma*: "Nospiediet BACK, lai atceltu."
cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Nospiediet POWER, lai atceltu."
ihifi760,ihifi960: "Divreiz pieskarieties RETURN, lai atceltu."
ihifi770,ihifi770c,ihifi800: "Nospiediet HOME, lai atceltu."
@ -14811,7 +14811,7 @@
</dest>
<voice>
*: "Nospiediet LEFT, lai atceltu."
android,hifietma*,zenvision: "Nospiediet BACK, lai atceltu."
android,hifietma*: "Nospiediet BACK, lai atceltu."
cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Nospiediet POWER, lai atceltu."
ihifi760,ihifi960: "Divreiz pieskarieties RETURN, lai atceltu."
ihifi770,ihifi770c,ihifi800: "Nospiediet HOME, lai atceltu."
@ -15306,13 +15306,13 @@
</phrase>
<phrase>
id: LANG_VOICED_DATE_FORMAT
desc: format string for how dates will be read back. Y == 4-digit year, A == month name, m == numeric month, d == numeric day. For example, "AdY" will read "January 21 2021"
desc: format string for how dates will be read back. Y == 4-digit year (grouped), y == 4-digit year (numeric), A == month name, m == numeric month, d == numeric day. For example, for 2021-01-05, "AdY" will be voiced as "January 5 twenty twenty-one" and "dmy" will be voiced as "5 1 two thousand twenty one
user: core
<source>
*: "dAY"
</source>
<dest>
*: "dAY"
*: "dAy"
</dest>
<voice>
*: ""
@ -15915,16 +15915,16 @@
</phrase>
<phrase>
id: LANG_DEFAULT_BROWSER
desc: in Settings
desc: deprecated
user: core
<source>
*: "Default Browser"
*: ""
</source>
<dest>
*: "Noklusējuma pārlūks"
*: ""
</dest>
<voice>
*: "Noklusējuma pārlūks"
*: ""
</voice>
</phrase>
<phrase>
@ -16973,3 +16973,129 @@
*: "Ju es bī"
</voice>
</phrase>
<phrase>
id: LANG_KEEP_DIRECTORY
desc: file browser setting
user: core
<source>
*: "Always remember last folder"
</source>
<dest>
*: "Vienmēr atcerēties pēdējo mapi"
</dest>
<voice>
*: "Vienmēr atcerēties pēdējo mapi"
</voice>
</phrase>
<phrase>
id: LANG_FILE_NOT_FOUND
desc: When file does not exist
user: core
<source>
*: "File not found"
</source>
<dest>
*: "Fails nav atrasts"
</dest>
<voice>
*: "Fails nav atrasts"
</voice>
</phrase>
<phrase>
id: LANG_SHOW_IN_FILES
desc: Reveal item in File Browser
user: core
<source>
*: "Show in Files"
</source>
<dest>
*: "Rādīt failu pārlūkā"
</dest>
<voice>
*: "Rādīt failu pārlūkā"
</voice>
</phrase>
<phrase>
id: LANG_CHANNEL_SWAP
desc: in sound_settings
user: core
<source>
*: "Swap Left & Right"
</source>
<dest>
*: "Apmainīt kanālus vietām"
</dest>
<voice>
*: "Apmainīt kanālus vietām"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_SET
desc: countdown_timer plugin - header shown on the setup screen where the user enters the countdown duration
user: core
<source>
*: "SET TIMER"
</source>
<dest>
*: "IESTATĪT TAIMERI"
</dest>
<voice>
*: "Iestatīt taimeri"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_RUNNING
desc: countdown_timer plugin - status label shown while the countdown is active
user: core
<source>
*: "RUNNING"
</source>
<dest>
*: "DARBOJAS"
</dest>
<voice>
*: "Darbojas"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_PAUSED
desc: countdown_timer plugin - status label shown while the countdown is paused
user: core
<source>
*: "PAUSED"
</source>
<dest>
*: "APTURĒTS"
</dest>
<voice>
*: "Apturēts"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_OVERTIME
desc: countdown_timer plugin - status label shown when the countdown has passed zero and is counting up
user: core
<source>
*: "OVERTIME"
</source>
<dest>
*: "PAPILDLAIKS"
</dest>
<voice>
*: "Papildlaiks"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_FINISHED
desc: countdown_timer plugin - status label shown at the moment the countdown expires
user: core
<source>
*: "FINISHED"
</source>
<dest>
*: "BEIDZIES"
</dest>
<voice>
*: "Beidzies"
</voice>
</phrase>

View file

@ -14780,7 +14780,7 @@
user: core
<source>
*: "Press LEFT to cancel."
android,hifietma*,zenvision: "Press BACK to cancel."
android,hifietma*: "Press BACK to cancel."
cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Press POWER to cancel."
ihifi760,ihifi960: "Double tap RETURN to cancel."
ihifi770,ihifi770c,ihifi800: "Press HOME to cancel."
@ -14795,7 +14795,7 @@
</source>
<dest>
*: "Притисните LEFT за прекид."
android,hifietma*,zenvision: "Притисните BACK за прекид."
android,hifietma*: "Притисните BACK за прекид."
cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Притисните POWER за прекид."
ihifi760,ihifi960: "Дупли тап RETURN за прекид."
ihifi770,ihifi770c,ihifi800: "Притисните HOME за прекид."
@ -14810,7 +14810,7 @@
</dest>
<voice>
*: "Притисните LEFT за прекид."
android,hifietma*,zenvision: "Притисните BACK за прекид."
android,hifietma*: "Притисните BACK за прекид."
cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Притисните POWER за прекид."
ihifi760,ihifi960: "Дупли тап RETURN за прекид."
ihifi770,ihifi770c,ihifi800: "Притисните HOME за прекид."
@ -15305,7 +15305,7 @@
</phrase>
<phrase>
id: LANG_VOICED_DATE_FORMAT
desc: format string for how dates will be read back. Y == 4-digit year, A == month name, m == numeric month, d == numeric day. For example, "AdY" will read "January 21 2021"
desc: format string for how dates will be read back. Y == 4-digit year (grouped), y == 4-digit year (numeric), A == month name, m == numeric month, d == numeric day. For example, for 2021-01-05, "AdY" will be voiced as "January 5 twenty twenty-one" and "dmy" will be voiced as "5 1 two thousand twenty one
user: core
<source>
*: "dAY"
@ -15914,16 +15914,16 @@
</phrase>
<phrase>
id: LANG_DEFAULT_BROWSER
desc: in Settings
desc: deprecated
user: core
<source>
*: "Default Browser"
*: ""
</source>
<dest>
*: "Подраз. прегледач"
*: ""
</dest>
<voice>
*: "Подраз. прегледач"
*: ""
</voice>
</phrase>
<phrase>
@ -16972,3 +16972,129 @@
*: "У Ес Бе"
</voice>
</phrase>
<phrase>
id: LANG_KEEP_DIRECTORY
desc: file browser setting
user: core
<source>
*: "Always remember last folder"
</source>
<dest>
*: "Увек памти последњи фолдер"
</dest>
<voice>
*: "Увек памти последњи фолдер"
</voice>
</phrase>
<phrase>
id: LANG_FILE_NOT_FOUND
desc: When file does not exist
user: core
<source>
*: "File not found"
</source>
<dest>
*: "Фајл није пронађен"
</dest>
<voice>
*: "Фајл није пронађен"
</voice>
</phrase>
<phrase>
id: LANG_SHOW_IN_FILES
desc: Reveal item in File Browser
user: core
<source>
*: "Show in Files"
</source>
<dest>
*: "Прикажи у фајловима"
</dest>
<voice>
*: "Прикажи у фајловима"
</voice>
</phrase>
<phrase>
id: LANG_CHANNEL_SWAP
desc: in sound_settings
user: core
<source>
*: "Swap Left & Right"
</source>
<dest>
*: "Замени леви и десни"
</dest>
<voice>
*: "Замени леви и десни"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_SET
desc: countdown_timer plugin - header shown on the setup screen where the user enters the countdown duration
user: core
<source>
*: "SET TIMER"
</source>
<dest>
*: "ПОСТАВИ ТАЈМЕР"
</dest>
<voice>
*: "Постави тајмер"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_RUNNING
desc: countdown_timer plugin - status label shown while the countdown is active
user: core
<source>
*: "RUNNING"
</source>
<dest>
*: "ОДБРОЈАВАЊЕ"
</dest>
<voice>
*: "Одбројавање"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_PAUSED
desc: countdown_timer plugin - status label shown while the countdown is paused
user: core
<source>
*: "PAUSED"
</source>
<dest>
*: "ПАУЗИРАНО"
</dest>
<voice>
*: "Паузирано"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_OVERTIME
desc: countdown_timer plugin - status label shown when the countdown has passed zero and is counting up
user: core
<source>
*: "OVERTIME"
</source>
<dest>
*: "ИСТЕКЛО ВРЕМЕ"
</dest>
<voice>
*: "Истекло време"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_FINISHED
desc: countdown_timer plugin - status label shown at the moment the countdown expires
user: core
<source>
*: "FINISHED"
</source>
<dest>
*: "ГОТОВО"
</dest>
<voice>
*: "Готово"
</voice>
</phrase>

View file

@ -442,13 +442,10 @@ enum eq_type {
HIGH_SHELF
};
/* Size of just the slider/srollbar */
#define SCROLLBAR_SIZE 6
/* Draw the UI for a whole EQ band */
static int draw_eq_slider(struct screen * screen, int x, int y,
int width, int cutoff, int q, int gain, bool selected,
enum eq_slider_mode mode, int band)
enum eq_slider_mode mode, int band, int scrollbar_size)
{
char buf[26];
int steps, min_item, max_item;
@ -534,14 +531,14 @@ static int draw_eq_slider(struct screen * screen, int x, int y,
screen->putsxy(x1, y1, buf);
/* Draw selection box */
total_height = 3 + h + 1 + SCROLLBAR_SIZE + 3;
total_height = 3 + h + 1 + scrollbar_size + 3;
screen->set_drawmode(DRMODE_SOLID);
if (selected) {
screen->drawrect(x, y, width, total_height);
}
/* Draw horizontal slider. Reuse scrollbar for this */
gui_scrollbar_draw(screen, x + 3, y1 + h + 1, width - 6, SCROLLBAR_SIZE,
gui_scrollbar_draw(screen, x + 3, y1 + h + 1, width - 6, scrollbar_size,
steps, min_item, max_item, HORIZONTAL);
return total_height;
@ -550,7 +547,7 @@ static int draw_eq_slider(struct screen * screen, int x, int y,
/* Draw's all the EQ sliders. Returns the total height of the sliders drawn */
static void draw_eq_sliders(struct screen * screen, int x, int y,
int nb_eq_sliders, int start_item,
int current_band, enum eq_slider_mode mode)
int current_band, enum eq_slider_mode mode, int scrollbar_size)
{
int height = y;
@ -568,14 +565,14 @@ static void draw_eq_sliders(struct screen * screen, int x, int y,
if (i >= start_item) {
height += draw_eq_slider(screen, x, height, screen->lcdwidth - x - 1,
cutoff, q, gain, i == current_band, mode,
i);
i, scrollbar_size);
/* add a margin */
height++;
}
}
if (nb_eq_sliders != EQ_NUM_BANDS)
gui_scrollbar_draw(screen, 0, y, SCROLLBAR_SIZE - 1,
gui_scrollbar_draw(screen, 0, y, scrollbar_size - 1,
screen->lcdheight - y, EQ_NUM_BANDS,
start_item, start_item + nb_eq_sliders,
VERTICAL);
@ -593,20 +590,23 @@ int eq_menu_graphical(void)
int current_band, x, y, step, fast_step, min, max;
enum eq_slider_mode mode;
int h, height, start_item, nb_eq_sliders[NB_SCREENS];
int scrollbar_size[NB_SCREENS];
FOR_NB_SCREENS(i)
viewportmanager_theme_enable(i, false, NULL);
FOR_NB_SCREENS(i) {
screens[i].set_viewport(NULL);
screens[i].setfont(FONT_SYSFIXED);
screens[i].clear_display();
/* Figure out how many sliders can be drawn on the screen */
h = screens[i].getcharheight();
scrollbar_size[i] = h * 3 / 4;
if (scrollbar_size[i] < 6)
scrollbar_size[i] = 6;
/* Total height includes margins (1), text, slider, and line selector (1) */
height = 3 + h + 1 + SCROLLBAR_SIZE + 3;
height = 3 + h + 1 + scrollbar_size[i] + 3;
nb_eq_sliders[i] = screens[i].lcdheight / height;
/* Make sure the "Edit Mode" text fits too */
@ -675,14 +675,14 @@ int eq_menu_graphical(void)
} else {
start_item = current_band - 1;
}
x = SCROLLBAR_SIZE;
x = scrollbar_size[i];
} else {
x = 1;
start_item = 0;
}
/* Draw equalizer band details */
draw_eq_sliders(&screens[i], x, y, nb_eq_sliders[i], start_item,
current_band, mode);
current_band, mode, scrollbar_size[i]);
screens[i].update();
}
@ -761,7 +761,6 @@ int eq_menu_graphical(void)
/* Reset screen settings */
FOR_NB_SCREENS(i)
{
screens[i].setfont(FONT_UI);
screens[i].clear_display();
screens[i].set_viewport(NULL);
viewportmanager_theme_undo(i, false);

View file

@ -29,6 +29,7 @@
#include "system.h"
#include "lcd.h"
#include "language.h" /* is_lang_rtl() */
#include "iap-usb.h"
#ifdef HAVE_DIRCACHE
#include "dircache.h"
@ -64,6 +65,7 @@
#include "list.h"
#include "fixedpoint.h"
#include "open_plugin.h"
#include "statusbar-skinned.h"
#include "debug.h"
@ -870,6 +872,7 @@ void check_bootfile(bool do_rolo)
void setvol(void)
{
sound_set_volume(global_status.volume);
iap_on_volume(global_status.volume);
global_status.last_volume_change = current_tick;
status_save(false);
}
@ -1793,8 +1796,14 @@ static void push_current_activity_refresh(enum current_activity screen, bool ref
{
skinlist_set_cfg(i, NULL);
if (refresh)
{
skin_defer_rendering(true);
skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL);
skin_defer_rendering(false);
}
}
if (refresh)
sb_skin_force_next_update();
}
static void pop_current_activity_refresh(bool refresh)
@ -1804,8 +1813,14 @@ static void pop_current_activity_refresh(bool refresh)
{
skinlist_set_cfg(i, NULL);
if (refresh)
{
skin_defer_rendering(true);
skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL);
skin_defer_rendering(false);
}
}
if (refresh)
sb_skin_force_next_update();
}
void push_current_activity(enum current_activity screen)

View file

@ -1287,6 +1287,7 @@ static int hotkey_tree_run_plugin(void *param)
return ONPLAY_RELOAD_DIR;
}
static int hotkey_run_menu(void); /* display hotkey items as a menu */
static int hotkey_wps_run_plugin(void)
{
open_plugin_run(ID2P(LANG_HOTKEY_WPS));
@ -1297,6 +1298,7 @@ static int hotkey_wps_run_plugin(void)
/* Any desired hotkey functions go here, in the enum in onplay.h,
and in the settings menu in settings_list.c. The order here
is not important. */
static const struct hotkey_assignment hotkey_items[] = {
[0]{ .action = HOTKEY_OFF,
.lang_id = LANG_OFF,
@ -1360,6 +1362,18 @@ static const struct hotkey_assignment hotkey_items[] = {
.func = HOTKEY_FUNC(hotkey_tree_run_plugin, (void *)"properties"),
.return_code = ONPLAY_FUNC_RETURN,
.flags = HOTKEY_FLAG_TREE },
{ .action = HOTKEY_CONTEXT_MENU,
.lang_id = LANG_ONPLAY_MENU_TITLE,
.func = HOTKEY_FUNC(hotkey_run_menu, NULL),
.return_code = ONPLAY_FUNC_RETURN,
.flags = HOTKEY_FLAG_WPS | HOTKEY_FLAG_TREE },
#ifdef HAVE_ALBUMART
{ .action = HOTKEY_ALBUMART,
.lang_id = LANG_VIEW_ALBUMART,
.func = HOTKEY_FUNC(view_album_art, NULL),
.return_code = ONPLAY_OK,
.flags = HOTKEY_FLAG_WPS | HOTKEY_FLAG_NOSBS },
#endif
#ifdef HAVE_TAGCACHE
{ .action = HOTKEY_PICTUREFLOW,
.lang_id = LANG_ONPLAY_PICTUREFLOW,
@ -1380,11 +1394,8 @@ const struct hotkey_assignment *get_hotkey(int action)
}
/* Execute the hotkey function, if listed */
static int execute_hotkey(bool is_wps)
static int execute_hotkey(int action)
{
const int action = (is_wps ? global_settings.hotkey_wps :
global_settings.hotkey_tree);
/* search assignment struct for a match for the hotkey setting */
const struct hotkey_assignment *this_item = get_hotkey(action);
@ -1405,6 +1416,60 @@ static int execute_hotkey(bool is_wps)
return func_return; /* Use value returned by function */
return return_code; /* or return the associated value */
}
static const char* hotkey_get_name(int selected_item, void * data,
char * buffer, size_t buffer_len)
{
(void)buffer; (void)buffer_len;
const struct hotkey_assignment **hk_menu = (const struct hotkey_assignment **)data;
return ID2P(hk_menu[selected_item + 1]->lang_id); /* +1 to skip HOTKEY_OFF */
}
static int hotkey_get_talk(int selected_item, void * data)
{
if (global_settings.talk_menu)
{
const struct hotkey_assignment **hk_menu = (const struct hotkey_assignment **)data;
talk_id(hk_menu[selected_item + 1]->lang_id, false); /* +1 to skip HOTKEY_OFF */
}
return 0;
}
static int hotkey_run_menu(void)
{
intptr_t flag = HOTKEY_FLAG_WPS;
if (selected_file.context != CONTEXT_WPS)
flag = HOTKEY_FLAG_TREE;
const struct hotkey_assignment *hk_menu[ARRAYLEN(hotkey_items)];
struct simplelist_info info;
int count = 0;
for (size_t i = 0; i < ARRAYLEN(hotkey_items); i++)
{
hk_menu[i] = NULL; /*clear all the hk_menu entries prior to setting them */
if ((hotkey_items[i].flags & flag) == flag)
{
if (hotkey_items[i].action != HOTKEY_CONTEXT_MENU)
{
hk_menu[count++] = &hotkey_items[i];
}
}
}
/* count -1 don't display HOTKEY_OFF item */
simplelist_info_init(&info, str(LANG_ONPLAY_MENU_TITLE), count - 1, (void*)&hk_menu);
info.get_name = hotkey_get_name;
info.get_icon = NULL;
info.get_talk = hotkey_get_talk;
simplelist_show_list(&info);
if (info.selection >= 0) /* run user selected hotkey item */
{
return execute_hotkey(hk_menu[info.selection + 1]->action);
}
return ONPLAY_RELOAD_DIR;
}
#endif /* HOTKEY */
int onplay(char* file, int attr, int from_context, bool hotkey, int customaction)
@ -1441,7 +1506,8 @@ int onplay(char* file, int attr, int from_context, bool hotkey, int customaction
#ifdef HAVE_HOTKEY
if (hotkey)
return execute_hotkey(from_context == CONTEXT_WPS);
return execute_hotkey((from_context == CONTEXT_WPS ?
global_settings.hotkey_wps : global_settings.hotkey_tree));
#else
(void)hotkey;
#endif

View file

@ -63,6 +63,8 @@ enum hotkey_action {
HOTKEY_INSERT,
HOTKEY_INSERT_SHUFFLED,
HOTKEY_BOOKMARK_LIST,
HOTKEY_ALBUMART,
HOTKEY_CONTEXT_MENU, /* shows / executes above actions in a menu */
};
enum hotkey_flags {
HOTKEY_FLAG_NONE = 0x0,

View file

@ -35,7 +35,7 @@
static const uint32_t open_plugin_csum = OPEN_PLUGIN_CHECKSUM;
static const int op_entry_sz = sizeof(struct open_plugin_entry_t);
#define OP_ENTRY_SZ (sizeof(struct open_plugin_entry_t))
static const char* strip_rockbox_root(const char *path)
{
@ -49,7 +49,7 @@ static inline void op_clear_entry(struct open_plugin_entry_t *entry)
{
if (entry == NULL)
return;
memset(entry, 0, op_entry_sz);
memset(entry, 0, OP_ENTRY_SZ);
entry->lang_id = OPEN_PLUGIN_LANG_INVALID;
}
@ -78,7 +78,7 @@ static int op_find_entry(int fd, struct open_plugin_entry_t *entry,
{
logf("OP find_entry *Searching* hash: %x lang_id: %d", hash, lang_id);
while (read(fd, entry, op_entry_sz) == op_entry_sz)
while (read(fd, entry, OP_ENTRY_SZ) == OP_ENTRY_SZ)
{
if (entry->lang_id == lang_id || entry->hash == hash ||
(lang_id == OPEN_PLUGIN_LANG_IGNOREALL))/* return first entry found */
@ -167,9 +167,9 @@ static int op_update_dat(struct open_plugin_entry_t *entry, bool clear)
lseek(fd, 0-hlc_sz, SEEK_CUR);/* back to the start of record */
break;
}
lseek(fd, op_entry_sz - hlc_sz, SEEK_CUR); /* finish record */
lseek(fd, OP_ENTRY_SZ - hlc_sz, SEEK_CUR); /* finish record */
}
write(fd, entry, op_entry_sz);
write(fd, entry, OP_ENTRY_SZ);
close(fd);
#else /* Everyone else make a temp file */
logf("OP update *Copying entries* %s", OPEN_PLUGIN_DAT ".tmp");
@ -177,18 +177,18 @@ static int op_update_dat(struct open_plugin_entry_t *entry, bool clear)
if (fd < 0)
return OPEN_PLUGIN_NOT_FOUND;
write(fd, entry, op_entry_sz);
write(fd, entry, OP_ENTRY_SZ);
int fd1 = open(OPEN_PLUGIN_DAT, O_RDONLY);
if (fd1 >= 0)
{
/* copy non-duplicate entries back from original */
while (read(fd1, entry, op_entry_sz) == op_entry_sz)
while (read(fd1, entry, OP_ENTRY_SZ) == OP_ENTRY_SZ)
{
if (entry->hash != hash && entry->lang_id != lang_id &&
op_entry_checksum(entry) > 0)
{
write(fd, entry, op_entry_sz);
write(fd, entry, OP_ENTRY_SZ);
}
}
close(fd1);
@ -529,41 +529,13 @@ static bool op_entry_read(int fd, int selected_item, off_t data_sz, struct open_
{
op_clear_entry(op_entry);
return ((selected_item >= 0) && (fd >= 0) &&
(lseek(fd, selected_item * op_entry_sz, SEEK_SET) >= 0) &&
(lseek(fd, selected_item * OP_ENTRY_SZ, SEEK_SET) >= 0) &&
(read(fd, op_entry, data_sz) == data_sz) && op_entry_checksum(op_entry) > 0);
}
void open_plugin_import(char *strdat)
void open_plugin_import(const char *cfg_file)
{
/* Note: Destroys strdat */
char *vect[5];
/* expected openplugin strdat: "englishid", "name", "path", "param" */
if (split_string(strdat, ',', vect, 5) == 4)
{
/* Space for 5 values so we will fail if excess arguments or too few */
for(int i = 0; i < 4; i++)
{
vect[i] = skip_whitespace(vect[i]);
int eos = ((int)strlen(vect[i]))-2;
/* Failure if string is not quoted */
if (vect[i][0] != '"' || eos < 0 || vect[i][eos + 1] != '"')
{
logf("%s[%d] error: quoted string expected\n", __func__, i);
return;
}
vect[i]++; /* skip first " */
vect[i][eos] = '\0'; /* skip other " */
}
char *key = vect[1];
int lang_id = lang_english_to_id(vect[0]);
if (lang_id >= 0)
key = ID2P(lang_id);
/* if key exists it will be overwritten */
open_plugin_add_path(key, vect[2], vect[3]);
return;
}
logf("%s error: importing entries", __func__);
plugin_load(VIEWERS_DIR"/open_plugins.rock", cfg_file);
}
void open_plugin_export(int cfg_fd)
@ -584,7 +556,7 @@ void open_plugin_export(int cfg_fd)
logf("%s error: opening %s", __func__, OPEN_PLUGIN_DAT);
return; /* OPEN_PLUGIN_NOT_FOUND */
}
while (op_entry_read(fd, index++, op_entry_sz, op_entry))
while (op_entry_read(fd, index++, OP_ENTRY_SZ, op_entry))
{
/* don't save the LANG_OPEN_PLUGIN entry -- it is for internal use */
if (op_entry->lang_id == LANG_OPEN_PLUGIN)
@ -596,8 +568,16 @@ void open_plugin_export(int cfg_fd)
else
lang_name = "[USER]"; /* needs to be an invalid lang string */
fdprintf(cfg_fd,"%s: \"%s\", \"%s\", \"%s\", \"%s\"\n",
"openplugin", lang_name, op_entry->name, op_entry->path, op_entry->param);
bool dblquote = strchr(op_entry->name, '"') != NULL ||
strchr(op_entry->param, '"') != NULL;
const char* fmtstr = "%s: \"%s\", \"%s\", \"%s\", \"%s\"\n";
if (dblquote) /* if using double quotes export with single quotes */
{
fmtstr = "%s: '%s', '%s', '%s', '%s'\n";
}
fdprintf(cfg_fd, fmtstr, OPEN_PLUGIN_CFGNAME,
lang_name, op_entry->name, op_entry->path, op_entry->param);
logf("openplugin[%d]: \"%s\", \"%s\", \"%s\", \"%s\"\n lang id: %d hash: %x, csum: %x\n",
index, lang_name, op_entry->name, op_entry->path, op_entry->param,

View file

@ -35,6 +35,7 @@
#define OPEN_RBPLUGIN_DAT PLUGIN_DIR "/rb_plugins.dat"
#define OPEN_PLUGIN_BUFSZ MAX_PATH
#define OPEN_PLUGIN_NAMESZ 32
#define OPEN_PLUGIN_CFGNAME "openplugin"
enum {
OPEN_PLUGIN_LANG_INVALID = (-1),
@ -84,7 +85,7 @@ int open_plugin_load_entry(const char *key);
void open_plugin_browse(const char *key);
int open_plugin_run(const char *key);
void open_plugin_cache_flush(void); /* flush to disk */
void open_plugin_import(char *strdat);
void open_plugin_import(const char *cfg_file);
void open_plugin_export(int cfg_fd);
#endif

View file

@ -48,6 +48,7 @@
#include "settings.h"
#include "audiohw.h"
#include "general.h"
#include "iap-usb.h"
#include <stdio.h>
#ifdef HAVE_TAGCACHE
@ -178,7 +179,11 @@ struct audio_resume_info
static struct mutex id3_mutex SHAREDBSS_ATTR; /* (A,O)*/
/** For album art support **/
#if defined(USB_ENABLE_IAP)
#define MAX_MULTIPLE_AA (SKINNABLE_SCREENS_COUNT + 1)
#else
#define MAX_MULTIPLE_AA SKINNABLE_SCREENS_COUNT
#endif
#ifdef HAVE_ALBUMART
static int albumart_mode = -1;
@ -1407,6 +1412,7 @@ static void audio_playlist_track_change(void)
{
send_track_event(PLAYBACK_EVENT_TRACK_CHANGE,
track_event_flags, id3);
iap_on_track_playback_index(playlist_get_display_index() - 1, false);
}
position_key = pcmbuf_get_position_key();
@ -2315,6 +2321,8 @@ static int audio_finish_load_track(struct track_info *infop)
by the time PLAYBACK_EVENT_TRACK_CHANGE is sent */
send_track_event(PLAYBACK_EVENT_CUR_TRACK_READY, 0,
id3_get(PLAYING_ID3));
/* Also send pending notification */
iap_on_track_playback_index(playlist_get_display_index() - 1, true);
}
#ifdef HAVE_CODEC_BUFFERING
@ -2742,6 +2750,7 @@ static void audio_finalise_track_change(void)
if (pause_on_track_change || single_mode_do_pause(info.id3_hid))
{
play_status = PLAY_PAUSED;
iap_on_play_status(play_status);
pcmbuf_pause(true);
pause_on_track_change = false;
}
@ -3075,6 +3084,7 @@ static void audio_start_playback(const struct audio_resume_info *resume_info,
/* Update our state */
play_status = PLAY_PLAYING;
iap_on_play_status(play_status);
}
/* Codec's position should be available as soon as it knows it */
@ -3162,6 +3172,7 @@ static void audio_stop_playback(void)
/* Update our state */
ff_rw_mode = false;
play_status = PLAY_STOPPED;
iap_on_play_status(play_status);
wipe_track_metadata(true);
#ifdef HAVE_ALBUMART
@ -3183,6 +3194,7 @@ static void audio_on_pause(bool pause)
return;
play_status = pause ? PLAY_PAUSED : PLAY_PLAYING;
iap_on_play_status(play_status);
if (!pause && codec_skip_pending)
{
@ -3824,6 +3836,7 @@ void audio_pcmbuf_position_callback(unsigned long elapsed, off_t offset,
struct mp3entry *id3 = id3_get(PLAYING_ID3);
id3->elapsed = elapsed;
id3->offset = offset;
iap_on_track_time_position(elapsed);
}
}
@ -4234,12 +4247,22 @@ void audio_set_crossfade(int enable)
static unsigned long audio_guess_frequency(struct mp3entry *id3)
{
const struct pcm_sink_caps* caps = pcm_sink_caps(pcm_current_sink());
bool have_44 = false;
bool have_48 = false;
for (size_t i = 0; i < caps->num_samprs; i += 1)
{
if (caps->samprs[i] == SAMPR_44)
have_44 = true;
if (caps->samprs[i] == SAMPR_48)
have_48 = true;
if (id3->frequency == caps->samprs[i])
return id3->frequency;
}
return (id3->frequency % 4000) ? SAMPR_44 : SAMPR_48;
unsigned long fallback = (id3->frequency % 4000) ? SAMPR_44 : SAMPR_48;
if ((fallback == SAMPR_44 && have_44) || (fallback == SAMPR_48 && have_48))
return fallback;
else
return caps->samprs[caps->default_freq];
}
static bool audio_auto_change_frequency(struct mp3entry *id3, bool play)

View file

@ -107,6 +107,7 @@
#include "rbunicode.h"
#include "root_menu.h"
#include "plugin.h" /* To borrow a temp buffer to rewrite a .m3u8 file */
#include "iap-usb.h"
#include "logdiskf.h"
#ifdef HAVE_DIRCACHE
#include "dircache.h"
@ -315,7 +316,7 @@ static void pl_close_control(struct playlist_info *playlist)
/* Check if the filename suggests M3U or M3U8 format. */
static bool is_m3u8_name(const char* filename)
{
char *dot = strrchr(filename, '.');
const char *dot = strrchr(filename, '.');
/* Default to M3U8 unless explicitly told otherwise. */
return (!dot || strcasecmp(dot, ".m3u") != 0);
@ -1217,6 +1218,8 @@ static int remove_all_tracks_unlocked(struct playlist_info *playlist)
playlist->amount = 1;
playlist->indices[0] |= PLAYLIST_QUEUED;
playlist->flags = 0; /* Reset dirplay and modified flags */
if (playlist == &current_playlist)
iap_on_tracks_count(playlist->amount);
if (playlist->last_insert_pos == 0)
playlist->last_insert_pos = -1;
@ -1424,6 +1427,8 @@ static int add_track_to_playlist_unlocked(struct playlist_info* playlist,
dc_init_filerefs(playlist, insert_position, 1);
playlist->amount++;
if (playlist == &current_playlist)
iap_on_tracks_count(playlist->amount);
return insert_position;
}
@ -1506,6 +1511,9 @@ static void find_and_set_playlist_index_unlocked(struct playlist_info* playlist,
{
playlist->index = playlist->first_index = i;
if (playlist == &current_playlist)
iap_on_track_playback_index(rotate_index(playlist, playlist->index), true);
break;
}
}
@ -2949,6 +2957,7 @@ int playlist_next(int steps)
sort_playlist_unlocked(playlist, false, false);
randomise_playlist_unlocked(playlist, current_tick, false, true);
global_settings.playlist_shuffle = true;
iap_on_shuffle_state(global_settings.playlist_shuffle);
playlist->started = true;
playlist->index = 0;
@ -3537,8 +3546,9 @@ int playlist_resume(void)
}
}
if (global_status.resume_index != -1)
if (global_status.resume_index != -1) {
playlist->index = global_status.resume_index;
}
out:
playlist_write_unlock(playlist);
@ -4047,8 +4057,10 @@ static int pl_save_update_control(struct playlist_info* playlist,
/* Reset shuffle seed */
playlist->seed = 0;
if (playlist == &current_playlist)
if (playlist == &current_playlist) {
global_settings.playlist_shuffle = false;
iap_on_shuffle_state(global_settings.playlist_shuffle);
}
pl_close_control(playlist);
close(old_fd);

View file

@ -52,6 +52,10 @@
#include "yesno.h"
#include "playback.h"
#if defined (HAVE_TAGCACHE) && defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
#include "tagcache.h"
#endif
/* Maximum number of tracks we can have loaded at one time */
#define MAX_PLAYLIST_ENTRIES 200
@ -124,6 +128,8 @@ struct playlist_viewer {
struct playlist_buffer buffer;
struct mp3entry *id3;
bool allow_view_text_plugin;
unsigned long loading_tick;
bool is_open;
};
struct playlist_search_data
@ -214,6 +220,13 @@ static void playlist_buffer_load_entries(struct playlist_buffer *pb, int index,
for (i = 0; i < num_entries; i++)
{
/* provide UI feedback if opening Playlist Viewer is taking too long */
if (!viewer.is_open && TIME_AFTER(current_tick, viewer.loading_tick))
{
viewer.loading_tick += HZ*10;
splash(0, ID2P(LANG_WAIT));
}
int len = playlist_entry_load(&(pb->tracks[i]), index, p, remaining);
if (len < 0)
{
@ -276,9 +289,15 @@ static bool retrieve_id3_tags(const int index, const char* name, struct mp3entry
copy_mp3entry(id3, audio_current_track()); /* retrieve id3 from RAM */
id3_retrieval_successful = true;
}
else
#if defined (HAVE_TAGCACHE) && defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
else if (flags & METADATA_EXCLUDE_ID3_PATH)
/* retrieve id3 from database */
id3_retrieval_successful = tagcache_fill_tags(id3, name);
#endif
if (!id3_retrieval_successful)
{
/* Read from disk, the database, doesn't store frequency, file size or codec (g4470) ChrisS*/
/* Read from disk: retrieves frequency, file size, and codec */
id3_retrieval_successful = get_metadata_ex(id3, -1, name, flags);
}
return id3_retrieval_successful;
@ -497,7 +516,7 @@ static void format_name(char* dest, const char* src, size_t bufsz)
default:
{
/* Only display the filename */
char* p = strrchr(src, '/');
const char* p = strrchr(src, '/');
strlcpy(dest, p+1, bufsz);
/* Remove the extension */
strrsplt(dest, '.');
@ -663,6 +682,7 @@ static void close_playlist_viewer(void)
}
playlist_close(viewer.playlist);
}
viewer.is_open = false;
}
#if defined(HAVE_HOTKEY) || defined(HAVE_TAGCACHE)
@ -960,9 +980,12 @@ static bool open_playlist_viewer(const char* filename,
struct gui_synclist *playlist_lists,
bool reload, int *most_recent_selection)
{
viewer.loading_tick = current_tick + HZ/3;
push_current_activity(ACTIVITY_PLAYLISTVIEWER);
if (!playlist_viewer_init(&viewer, filename, reload, most_recent_selection))
if (playlist_viewer_init(&viewer, filename, reload, most_recent_selection))
viewer.is_open = true;
else
return false;
update_gui(playlist_lists, true);

View file

@ -49,6 +49,7 @@
#include "core_keymap.h"
#include "language.h"
#include "statusbar-skinned.h"
#include "panic.h"
#if CONFIG_CHARGING
#include "power.h"
@ -870,6 +871,10 @@ static const struct plugin_api rockbox_api = {
/* new stuff at the end, sort into place next time
the API gets incompatible */
panicf,
gui_synclist_scroll_stop,
add_event_ex,
remove_event_ex,
};
static int plugin_buffer_handle;
@ -890,7 +895,9 @@ int plugin_load(const char* plugin, const void* parameter)
!strcmp("playing_time.rock", sepch + 1) ||
!strcmp("main_menu_config.rock", sepch + 1) ||
!strcmp("text_viewer.rock", sepch + 1) ||
!strcmp("disktidy.rock", sepch + 1));
!strcmp("view_text.rock", sepch + 1) ||
!strcmp("disktidy.rock", sepch + 1) ||
!strcmp("open_plugins.rock", sepch + 1));
if (current_plugin_handle)
{
@ -1015,8 +1022,11 @@ int plugin_load(const char* plugin, const void* parameter)
pop_current_activity_without_refresh();
if (get_current_activity() != ACTIVITY_WPS)
{
skin_defer_rendering(true);
FOR_NB_SCREENS(i)
skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_ALL);
skin_defer_rendering(false);
sb_skin_force_next_update();
}
if (!pfn_tsr_exit)

View file

@ -45,6 +45,8 @@
#undef strcmp
#undef strncmp
#undef strchr
#undef strstr
#undef strrchr
#undef strtok_r
#ifdef __APPLE__
#undef strncpy
@ -1024,6 +1026,18 @@ struct plugin_api {
/* new stuff at the end, sort into place next time
the API gets incompatible */
void (*panicf)(const char *msg, ...);
void (*gui_synclist_scroll_stop)(struct gui_synclist *lists);
bool (*add_event_ex)(unsigned short id, bool oneshot,
void (*handler)(unsigned short id,
void *event_data,
void *user_data),
void *user_data);
void (*remove_event_ex)(unsigned short id,
void (*handler)(unsigned short id,
void *event_data,
void *user_data),
void *user_data);
};
/* plugin header */

View file

@ -58,4 +58,12 @@ $(BUILDDIR)/apps/plugins/bitmaps/remote_native/%.c: $(ROOTDIR)/apps/plugins/bitm
$(SILENT)mkdir -p $(dir $@) $(PBMPINCDIR)
$(call PRINTS,BMP2RB $(<F))$(BMP2RB_REMOTENATIVE) -b -h $(PBMPINCDIR) $< > $@
ifdef APP_TYPE
# Bitmaps must be explicitly Position independent to avoid linker warnings
$(BUILDDIR)/apps/plugins/bitmaps/native/%.o: CFLAGS += -fPIC
$(BUILDDIR)/apps/plugins/bitmaps/mono/%.o: CFLAGS += -fPIC
$(BUILDDIR)/apps/plugins/bitmaps/remote_mono/%.o: CFLAGS += -fPIC
$(BUILDDIR)/apps/plugins/bitmaps/remote_native/%.o: CFLAGS += -fPIC
endif
endif

View file

@ -519,6 +519,7 @@ F3: equal to "="
#define CALCULATOR_DOWN BUTTON_DOWN
#define CALCULATOR_QUIT BUTTON_POWER
#define CALCULATOR_INPUT BUTTON_PLAY
#define CALCULATOR_INPUT2 (BUTTON_SELECT|BUTTON_REL)
#define CALCULATOR_CALC BUTTON_MENU
#define CALCULATOR_CLEAR BUTTON_BACK
@ -1938,6 +1939,9 @@ Handle buttons on basic screen
----------------------------------------------------------------------- */
static void basicButtonsProcess(void){
switch (btn) {
#ifdef CALCULATOR_INPUT2
case CALCULATOR_INPUT2: /*fallthrough*/
#endif
case CALCULATOR_INPUT:
if (calStatus == cal_error && (CAL_BUTTON != btn_C) ) break;
flashButton();
@ -2041,6 +2045,9 @@ Handle buttons on scientific screen
----------------------------------------------------------------------- */
static void sciButtonsProcess(void){
switch (btn) {
#ifdef CALCULATOR_INPUT2
case CALCULATOR_INPUT2: /*fallthrough*/
#endif
case CALCULATOR_INPUT:
if (calStatus == cal_error && (CAL_BUTTON != sci_sci) ) break;
flashButton();
@ -2133,6 +2140,9 @@ static int handleButton(int button){
break;
/* no unconditional break; here! */
#endif
#ifdef CALCULATOR_INPUT2 /* bypass pre button */
case CALCULATOR_INPUT2: /*fallthrough*/
#endif
#ifdef CALCULATOR_OPERATORS
case CALCULATOR_OPERATORS:
#endif

View file

@ -19,7 +19,7 @@ ifeq ($(findstring YES, $(call preprocess, $(APPSDIR)/plugins/BUILD_OVERLAY)), Y
### lowmem targets
ROCKS += $(CHESSBOX_OBJDIR)/chessbox.ovl
CHESSBOX_OUTLDS = $(CHESSBOX_OBJDIR)/chessbox.link
CHESSBOX_OVLFLAGS = -T$(CHESSBOX_OUTLDS) -Wl,--gc-sections -Wl,-Map,$(basename $@).map
CHESSBOX_OVLFLAGS = -T$(CHESSBOX_OUTLDS) -Wl,--gc-sections -Wl,-Map,$(basename $@).map $(GLOBAL_LDOPTS)
else
ROCKS += $(CHESSBOX_OBJDIR)/chessbox.rock
endif

View file

@ -103,18 +103,18 @@ static void aes_decrypt(void* data, uint32_t size)
static void calc_hash(uint32_t* data, uint32_t size, uint32_t* result)
{
uint32_t ptr, i;
uint32_t config = 2;
uint32_t config = SHA1_CONFIG_GO;
PWRCONEXT &= ~0x4;
for (ptr = 0; ptr < (size >> 2); ptr += 0x10)
{
for (i = 0; i < 0x10; i++) SHA1DATAIN[i] = data[ptr + i];
SHA1CONFIG = config;
config = 0xA;
while ((SHA1CONFIG & 1) != 0);
for (i = 0; i < 0x10; i++) SHA1_DATA[i] = data[ptr + i];
SHA1_CONFIG = config;
config = SHA1_CONFIG_CONT | SHA1_CONFIG_GO;
while ((SHA1_CONFIG & SHA1_CONFIG_BUSY) != 0);
}
for (i = 0; i < 5; i ++) result[i] = SHA1RESULT[i];
for (i = 0; i < 5; i ++) result[i] = SHA1_RESULT[i];
PWRCONEXT |= 0x4;
}

View file

@ -9,7 +9,7 @@
#define BUTTON_COLS 5
#define REC_HEIGHT (int)(LCD_HEIGHT / (BUTTON_ROWS + 1))
#define REC_WIDTH (int)(LCD_WIDTH / BUTTON_COLS)
#define REC_WIDTH (int)(LCD_WIDTH / BUTTON_COLS)
#define Y_7_POS (LCD_HEIGHT) /* Leave room for the border */
#define Y_6_POS (Y_7_POS - REC_HEIGHT) /* y6 = 63 */
@ -48,6 +48,7 @@
#define NUM_PLAYERS 2
#define MAX_UNDO 100
#define CENTRE_X (LCD_WIDTH/2)
static const struct button_mapping *plugin_contexts[] = {pla_main_ctx};
@ -72,12 +73,12 @@ static bool loaded = false; /* has a save been loaded? */
int btn_row, btn_col; /* current position index for button */
int prev_btn_row, prev_btn_col; /* previous cursor position */
unsigned char *buttonChar[6][5] = {
{"", "Single", "Double", "Triple", ""},
{"", "x1", "x2", "x3", ""},
{"1", "2", "3", "4", "5"},
{"6", "7", "8", "9", "10"},
{"11", "12", "13", "14", "15"},
{"16", "17", "18", "19", "20"},
{"", "Missed", "Bull", "Undo", ""}};
{"", "Miss", "Bull", "Undo", ""}};
int modifier;
static int do_dart_scorer_pause_menu(void);
@ -146,22 +147,34 @@ static void draw(void)
{
rb->lcd_clear_display();
char buf[32];
unsigned int w,h;
char buf[16];
int x = 5;
int y = 10;
for (int i = 0; i < NUM_PLAYERS; ++i, x = x + 95)
{
char *turn_marker = (i == settings.turn) ? "*" : "";
rb->snprintf(buf, sizeof(buf), "%sPlayer %d: %d", turn_marker, i + 1, settings.scores[i]);
rb->lcd_putsxy(x, y, buf);
}
int throws_x = (LCD_WIDTH / 2) - 10;
char throws_buf[3];
char *turn_marker;
/* player 1 score */
turn_marker = (0 == settings.turn) ? "*" : "";
#if LCD_HEIGHT <= 64 || LCD_WIDTH <= 96
rb->snprintf(buf, sizeof(buf), "%sP1 %d", turn_marker, settings.scores[0]);
#else
rb->snprintf(buf, sizeof(buf), "%sPlayer 1: %d", turn_marker, settings.scores[0]);
#endif
rb->lcd_putsxy(0, 10, buf);
/* player 2 score */
turn_marker = (1 == settings.turn) ? "*" : "";
#if LCD_HEIGHT <= 64 || LCD_WIDTH <= 96
rb->snprintf(buf, sizeof(buf), "%sP2 %d", turn_marker, settings.scores[1]);
#else
rb->snprintf(buf, sizeof(buf), "%sPlayer 2: %d", turn_marker, settings.scores[1]);
#endif
rb->lcd_getstringsize(buf, &w, &h);
rb->lcd_putsxy(LCD_WIDTH - w, 10, buf);
int throws_x = CENTRE_X - 5;
for (int i = 0; i < settings.throws; ++i, throws_x += 5)
{
rb->strcat(throws_buf, "1");
rb->lcd_putsxy(throws_x, y, "|");
rb->lcd_putsxy(throws_x, 10, "|");
}
drawButtons();
@ -324,11 +337,11 @@ static enum plugin_status do_game(bool newgame)
switch (button)
{
case DARTS_SELECT:
if ((!rb->strcmp(selected, "")) || (!rb->strcmp(selected, "Single")))
if ((!rb->strcmp(selected, "")) || (!rb->strcmp(selected, "x1")))
modifier = 1;
else if (!rb->strcmp(selected, "Double"))
else if (!rb->strcmp(selected, "x2"))
modifier = 2;
else if (!rb->strcmp(selected, "Triple"))
else if (!rb->strcmp(selected, "x3"))
modifier = 3;
else if (!rb->strcmp(selected, "Undo"))
{

View file

@ -474,6 +474,7 @@ void I_SubmitSound(void)
void I_ShutdownSound(void)
{
rb->pcmbuf_fade(false, false); /* Mute channel */
rb->mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK);
rb->mixer_set_frequency(HW_SAMPR_DEFAULT);
}
@ -508,6 +509,7 @@ void I_InitSound()
rb->audio_set_output_source(AUDIO_SRC_PLAYBACK);
#endif
rb->mixer_set_frequency(samplerate);
rb->pcmbuf_fade(false, true); /* Be sure channel is audible */
vol_lookup=malloc(128*256*sizeof(int));

View file

@ -276,7 +276,8 @@ static int get_image(struct image_info *info, int frame, int ds)
buf_images += size;
buf_images_size -= size;
if (!iv->running_slideshow)
if (!iv->settings->hide_info &&
!iv->running_slideshow)
{
rb->lcd_putsf(0, 3, "resizing %d*%d", info->width, info->height);
rb->lcd_update();

View file

@ -246,28 +246,32 @@ static bool set_option_dithering(void)
return false;
}
MENUITEM_FUNCTION(grayscale_item, 0, ID2P(LANG_GRAYSCALE),
set_option_grayscale, NULL, Icon_NOICON);
MENUITEM_FUNCTION(dithering_item, 0, ID2P(LANG_DITHERING),
set_option_dithering, NULL, Icon_NOICON);
#endif /* HAVE_LCD_COLOR */
static bool set_option_hide_info(void)
{
rb->set_bool(rb->str(LANG_HIDE_INFO), &settings.hide_info);
return false;
}
MENUITEM_FUNCTION(grayscale_item, 0, ID2P(LANG_GRAYSCALE),
set_option_grayscale, NULL, Icon_NOICON);
MENUITEM_FUNCTION(dithering_item, 0, ID2P(LANG_DITHERING),
set_option_dithering, NULL, Icon_NOICON);
MENUITEM_FUNCTION(hide_info_item, 0, ID2P(LANG_HIDE_INFO),
set_option_hide_info, NULL, Icon_NOICON);
MAKE_MENU(display_menu, ID2P(LANG_MENU_DISPLAY_OPTIONS), NULL, Icon_NOICON,
#ifdef HAVE_LCD_COLOR
&grayscale_item,
&dithering_item,
#endif /* HAVE_LCD_COLOR */
&hide_info_item);
static void display_options(void)
{
rb->do_menu(&display_menu, NULL, NULL, false);
}
#endif /* HAVE_LCD_COLOR */
static int show_menu(void) /* return 1 to quit */
{
@ -281,9 +285,7 @@ static int show_menu(void) /* return 1 to quit */
#ifdef USE_PLUG_BUF
MIID_SHOW_PLAYBACK_MENU,
#endif
#ifdef HAVE_LCD_COLOR
MIID_DISPLAY_OPTIONS,
#endif
MIID_QUIT,
};
@ -294,9 +296,7 @@ static int show_menu(void) /* return 1 to quit */
#ifdef USE_PLUG_BUF
ID2P(LANG_PLAYBACK_CONTROL),
#endif
#ifdef HAVE_LCD_COLOR
ID2P(LANG_MENU_DISPLAY_OPTIONS),
#endif
ID2P(LANG_MENU_QUIT));
static const struct opt_items slideshow[2] = {
@ -332,11 +332,9 @@ static int show_menu(void) /* return 1 to quit */
}
break;
#endif
#ifdef HAVE_LCD_COLOR
case MIID_DISPLAY_OPTIONS:
display_options();
break;
#endif
case MIID_QUIT:
return 1;
break;
@ -361,12 +359,6 @@ static int show_menu(void) /* return 1 to quit */
}
}
#endif
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
rb->lcd_set_foreground(LCD_WHITE);
rb->lcd_set_background(LCD_BLACK);
#endif
rb->lcd_clear_display();
return 0;
}
@ -776,9 +768,22 @@ static int scroll_bmp(struct image_info *info, bool initial_frame)
#ifdef USEGSLIB
grey_show(false); /* switch off greyscale overlay */
#endif
if (show_menu() == 1)
FOR_NB_SCREENS(i)
rb->viewportmanager_theme_enable(i, true, NULL);
int ret = show_menu();
FOR_NB_SCREENS(i)
rb->viewportmanager_theme_undo(i, false);
if (ret == 1)
return PLUGIN_OK;
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
rb->lcd_set_foreground(LCD_WHITE);
rb->lcd_set_background(LCD_BLACK);
#endif
rb->lcd_clear_display();
#ifdef USEGSLIB
grey_show(true); /* switch on greyscale overlay */
#else

View file

@ -34,6 +34,15 @@ void __aeabi_ldiv0(void) __attribute__((alias("__div0")));
#endif
#endif
#if defined(USE_STACK_PROTECTOR)
const uint32_t __stack_chk_guard = 0x3BADC0DE;
void __stack_chk_fail(void)
{
rb->panicf("plugin smashed stack");
}
#endif
void *memcpy(void *dest, const void *src, size_t n)
{
return rb->memcpy(dest, src, n);

View file

@ -29,13 +29,16 @@
struct view_info {
struct font* pf;
struct viewport scrollbar_vp; /* viewport for scrollbar */
struct viewport title_vp;
struct viewport vp;
bool sbs_has_title; /* SBS comes with custom title area */
const char *title;
const char *text; /* displayed text */
int display_lines; /* number of lines can be displayed */
int line_count; /* number of lines */
int line; /* current first line */
int start; /* possition of first line in text */
bool ui_update_cb;
};
static bool isbrchr(const unsigned char *str, int len)
@ -93,17 +96,19 @@ static void calc_line_count(struct view_info *info)
{
ptr = get_next_line(ptr, info);
i++;
if (!scrollbar && i > info->display_lines)
if (!scrollbar && i > info->display_lines &&
rb->global_settings->scrollbar != SCROLLBAR_OFF)
{
ptr = info->text;
i = 0;
info->scrollbar_vp = info->vp;
info->scrollbar_vp.width = rb->global_settings->scrollbar_width;
info->scrollbar_vp.width = rb->global_settings->scrollbar_width + 1;
info->scrollbar_vp.height = info->display_lines * info->pf->height;
info->vp.width -= info->scrollbar_vp.width;
if (rb->global_settings->scrollbar != SCROLLBAR_RIGHT)
info->vp.x = info->scrollbar_vp.width;
if (rb->global_settings->scrollbar == SCROLLBAR_RIGHT)
info->scrollbar_vp.x = info->vp.x + info->vp.width;
else
info->scrollbar_vp.x = info->vp.width;
info->vp.x += info->scrollbar_vp.width;
scrollbar = true;
}
}
@ -137,8 +142,14 @@ static void calc_first_line(struct view_info *info, int line)
static int init_view(struct view_info *info,
const char *title, const char *text)
{
rb->viewport_set_defaults(&info->vp, SCREEN_MAIN);
info->pf = rb->font_get(rb->screens[SCREEN_MAIN]->getuifont());
struct screen* display = rb->screens[SCREEN_MAIN];
int ui_font = display->getuifont();
display->set_viewport(&info->vp);
display->clear_viewport();
info->pf = rb->font_get(ui_font);
info->vp.font = ui_font;
info->display_lines = info->vp.height / info->pf->height;
info->title = title;
@ -146,17 +157,23 @@ static int init_view(struct view_info *info,
info->line_count = 0;
info->line = 0;
info->start = 0;
info->ui_update_cb = false;
/* no title for small screens. */
if (info->display_lines < 4)
if (!info->sbs_has_title)
{
info->title = NULL;
}
else
{
info->display_lines--;
info->vp.y += info->pf->height;
info->vp.height -= info->pf->height;
/* no title for small screens. */
if (info->display_lines < 4)
info->title = NULL;
else
{
info->display_lines--;
info->title_vp = info->vp;
info->title_vp.height = info->pf->height;
info->vp.height -= info->title_vp.height;
info->vp.y += info->title_vp.height;
}
}
calc_line_count(info);
@ -171,20 +188,19 @@ static void draw_text(struct view_info *info)
int max_show, line;
struct screen* display = rb->screens[SCREEN_MAIN];
/* clear screen */
display->clear_display();
/* display title. */
if(info->title)
if (info->title && !info->sbs_has_title)
{
display->set_viewport(NULL);
display->set_viewport(&info->title_vp);
display->puts(0, 0, info->title);
}
display->set_viewport(&info->vp);
display->clear_viewport();
max_show = MIN(info->line_count - info->line, info->display_lines);
text = info->text + info->start;
display->set_viewport(&info->vp);
for (line = 0; line < max_show; line++)
{
int len;
@ -197,17 +213,21 @@ static void draw_text(struct view_info *info)
display->puts(0, line, output);
text = ptr;
}
if (info->line_count > info->display_lines)
if (info->line_count > info->display_lines &&
rb->global_settings->scrollbar != SCROLLBAR_OFF)
{
display->set_viewport(&info->scrollbar_vp);
rb->gui_scrollbar_draw(display, (info->scrollbar_vp.width? 0: 1), 0,
info->scrollbar_vp.width - 1, info->scrollbar_vp.height,
info->line_count, info->line, info->line + max_show,
VERTICAL);
}
int margin = info->scrollbar_vp.width > 2 ? 2 : 0;
int x = rb->global_settings->scrollbar == SCROLLBAR_RIGHT ? margin : 0;
rb->gui_scrollbar_draw(display, x, 0,
info->scrollbar_vp.width - margin, info->scrollbar_vp.height,
info->line_count, info->line, info->line + max_show, VERTICAL);
}
display->set_viewport(NULL);
display->update();
if (!info->ui_update_cb)
display->update();
info->ui_update_cb = false;
}
static void scroll_up(struct view_info *info, int n)
@ -248,6 +268,22 @@ static void scroll_to_bottom(struct view_info *info)
draw_text(info);
}
static void ui_update_cb(unsigned short id, void* param, void* user_data)
{
(void)id;
(void)param;
struct view_info *info = (struct view_info *) user_data;
info->ui_update_cb = true;
draw_text(info);
}
static void cleanup(void *parameter)
{
struct view_info *info = (struct view_info *) parameter;
rb->remove_event_ex(GUI_EVENT_NEED_UI_UPDATE, ui_update_cb, info);
rb->viewportmanager_theme_undo(SCREEN_MAIN, false);
}
int view_text(const char *title, const char *text)
{
struct view_info info;
@ -256,13 +292,24 @@ int view_text(const char *title, const char *text)
};
int button;
info.sbs_has_title = rb->sb_set_persistent_title(title, Icon_NOICON, SCREEN_MAIN);
rb->viewportmanager_theme_enable(SCREEN_MAIN, true, &info.vp);
init_view(&info, title, text);
draw_text(&info);
/* handle themes that draw over the UI viewport */
rb->add_event_ex(GUI_EVENT_NEED_UI_UPDATE, false, ui_update_cb, &info);
/* skin engine needs to render title and redraw screen */
if (info.sbs_has_title)
rb->send_event(GUI_EVENT_ACTIONUPDATE, (void*)1);
else
draw_text(&info);
/* wait for keypress */
while(1)
{
button = pluginlib_getaction(TIMEOUT_BLOCK, view_contexts,
/* don't block, so the skin engine can redraw */
button = pluginlib_getaction(HZ/4, view_contexts,
ARRAYLEN(view_contexts));
switch (button)
{
@ -270,7 +317,7 @@ int view_text(const char *title, const char *text)
#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
|| (CONFIG_KEYPAD == IPOD_3G_PAD) \
|| (CONFIG_KEYPAD == IPOD_4G_PAD)
return PLUGIN_OK;
goto out;
#endif
case PLA_UP_REPEAT:
#ifdef HAVE_SCROLLWHEEL
@ -291,7 +338,7 @@ int view_text(const char *title, const char *text)
#if (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
|| (CONFIG_KEYPAD == IPOD_3G_PAD) \
|| (CONFIG_KEYPAD == IPOD_4G_PAD)
return PLUGIN_OK;
goto out;
#endif
scroll_up(&info, info.display_lines);
break;
@ -307,13 +354,14 @@ int view_text(const char *title, const char *text)
case PLA_SELECT:
case PLA_EXIT:
case PLA_CANCEL:
return PLUGIN_OK;
goto out;
default:
if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
if ((rb->default_event_handler_ex(button, cleanup,
&info) == SYS_USB_CONNECTED))
return PLUGIN_USB_CONNECTED;
break;
}
}
out:
cleanup(&info);
return PLUGIN_OK;
}

View file

@ -51,6 +51,10 @@ int splash_scroller(int timeout, const char* str);
#define floor lfloor
#define pow lpow
/* Just in case */
#undef memchr
#undef strstr
/* Simple substitutions */
#define malloc tlsf_malloc
#define realloc tlsf_realloc

View file

@ -349,7 +349,7 @@ static struct rocklua_image* rli_checktype(lua_State *L, int arg)
} /* rli_checktype */
static struct rocklua_image * alloc_rlimage(lua_State *L, bool alloc_data,
int width, int height)
int width, int height, size_t *allocd)
{
/* rliimage is pushed on the stack it is up to you to pop it */
struct rocklua_image *img;
@ -365,10 +365,24 @@ static struct rocklua_image * alloc_rlimage(lua_State *L, bool alloc_data,
n_elems = (size_t)(w_native * h_native);
if(alloc_data) /* if this a new image we need space for image data */
sz_data = n_elems * sizeof(fb_data);
if(alloc_data)
{/* if this a new image we need space for image data */
/* need even rows (see lcd-16bit-common.c for details) */
sz_data = BM_SIZE(width,height,FORMAT_NATIVE,false);
#if LCD_DEPTH > 24 /* account for possible 4bit alpha per pixel */
sz_data += ALIGN_UP(width, 2) * height / 2;
#endif
#if (LCD_DEPTH > 1) && defined(HAVE_BMP_SCALING)
if (width > BM_MAX_WIDTH)
sz_data += width*4;
#endif
sz_data = MAX(n_elems * sizeof(fb_data), sz_data);
}
if (allocd)
*allocd = sz_data;
/* newuserdata pushes the userdata onto the stack */
/*DEBUGF("rli alloc %d\n", sz_header + sz_data);*/
img = (struct rocklua_image *) lua_newuserdata(L, sz_header + sz_data);
luaL_getmetatable(L, ROCKLUA_IMAGE);
@ -386,15 +400,15 @@ static struct rocklua_image * alloc_rlimage(lua_State *L, bool alloc_data,
static inline void rli_wrap(lua_State *L, fb_data *src, int width, int height)
{
/* rliimage is pushed on the stack it is up to you to pop it */
struct rocklua_image *a = alloc_rlimage(L, false, width, height);
struct rocklua_image *a = alloc_rlimage(L, false, width, height, NULL);
a->data = src;
} /* rli_wrap */
static inline fb_data* rli_alloc(lua_State *L, int width, int height)
static inline fb_data* rli_alloc(lua_State *L, int width, int height, size_t *allocd)
{
/* rliimage is pushed on the stack it is up to you to pop it */
struct rocklua_image *a = alloc_rlimage(L, true, width, height);
struct rocklua_image *a = alloc_rlimage(L, true, width, height, allocd);
a->data = &a->dummy[0][0]; /* ref to beginning of alloc'd img data */
@ -995,7 +1009,7 @@ RLI_LUA rli_new(lua_State *L)
luaL_argcheck(L, width > 0 && height > 0, (width <= 0) ? 1 : 2, ERR_IDX_RANGE);
rli_alloc(L, width, height);
rli_alloc(L, width, height, NULL);
return 1;
}
@ -1904,10 +1918,13 @@ RB_WRAP(read_bmp_file)
int result = rb->read_bmp_file(filename, &bm, 0, format | FORMAT_RETURN_SIZE, NULL);
/*DEBUGF("read_bmp wants %d %d\n", result, BM_SIZE(bm.width, bm.height, format, false));*/
if(result > 0)
{
bm.data = (unsigned char*) rli_alloc(L, bm.width, bm.height);
if(rb->read_bmp_file(filename, &bm, result, format, NULL) < 0)
size_t sz_data;
bm.data = (unsigned char*) rli_alloc(L, bm.width, bm.height, &sz_data);
if(rb->read_bmp_file(filename, &bm, sz_data, format, NULL) < 0)
{
/* Error occured, drop newly allocated image from stack */
lua_pop(L, 1);

View file

@ -274,11 +274,11 @@ function bounce_image(img)
if IS_COLOR_TARGET then
if bit.band(loops, 128) == 128 then
_lcd:copy(imgn, x, y, 1, 1, fx, fy, false, _blit.BOR)
_lcd:copy(imgn, x, y, 1, 1, -fx, -fy, false, _blit.BOR)
_lcd:copy(screen_img, x, y, x, y, imgn:width(), imgn:height(),
false, _blit.BDEQC, imgn:get(1,1))
else
_lcd:copy(imgn, x, y, 1, 1, fx, fy, false, _blit.BSNEC, imgn:get(1,1))
_lcd:copy(imgn, x, y, 1, 1, -fx, -fy, false, _blit.BSNEC, imgn:get(1,1))
end
else
local blitop
@ -289,7 +289,7 @@ function bounce_image(img)
blitop = _blit.BXOR
end
_lcd:copy(imgn, x, y, 1, 1, fx, fy, false, blitop, WHITE)
_lcd:copy(imgn, x, y, 1, 1, -fx, -fy, false, blitop, WHITE)
end
if hold < 1 then
@ -674,6 +674,7 @@ end -- rotate_image
function flip_image(img)
local blitop = _blit.BOR
local i = 1
local d = 0
local x, y, w, h
@ -689,7 +690,7 @@ function flip_image(img)
--[[--Profiling code
local timer = _timer.start()]]
while d >= 0 do
while d >= 0 and img do
-- copy our flipped image onto the background
if d == 0 then
_lcd:copy(img, x, y, 1, 1, w, h, false, blitop)
@ -757,7 +758,7 @@ end -- draw_x
function random_img(img)
local min = _clr.set(0, 0, 0, 0)
local max = _clr.set(-1, 255, 255, 255)
math.randomseed(rb.current_tick())
--math.randomseed(rb.current_tick())
for x = 1, img:width() do
for y = 1, img:height() do
img:set(x, y, math.random(min, max))
@ -896,9 +897,13 @@ function main_menu()
[10] = long_text,
[11] = function(RAINB)
rainbow_img(_lcd()); _lcd:update(); rb.sleep(rb.HZ)
if rb.file_exists == nil or not rb.file_exists("/rainbow.bmp") then
_img_save(_LCD, "/rainbow.bmp")
end
end,
[12] = function(RANDM)
random_img(_lcd()); _lcd:update(); rb.sleep(rb.HZ)
_img_save(_LCD, "/random.bmp")
end,
[13] = function(CLEAR) _lcd:clear(BLACK); rock_lua() end,
[14] = function(SAVEI) _LCD:invert(); _img_save(_LCD, "/rocklua.bmp") end,
@ -927,6 +932,8 @@ _timer("main") -- keep track of how long the program ran
-- Clear the screen
_lcd:clear(BLACK)
math.randomseed(rb.current_tick())
if LCD_DEPTH > 1 then
--draw a gradient using available colors
if IS_COLOR_TARGET == true then

View file

@ -787,7 +787,6 @@ static bool beating = false; /* A beat is/was playing and count needs to increas
static int display_state = 0; /* Current display state code. */
static bool display_trigger = false; /* Draw display on next occasion */
static bool sound_active = false;
static bool sound_paused = true;
/* global static buffer for messages in any situation */
@ -1168,7 +1167,7 @@ static void timer_callback(void)
if(minitick >= period)
{
minitick = 0;
if(!sound_active && !sound_paused && !tap_count)
if(!sound_paused && !tap_count)
{
sound_trigger = true;
rb->reset_poweroff_timer();
@ -1213,6 +1212,7 @@ static void cleanup(void)
if(fd >= 0) rb->close(fd);
metronome_pause();
rb->pcmbuf_fade(false, false); /* Mute channel */
rb->mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK);
tweak_volume(0);
rb->led(0);
@ -1561,6 +1561,7 @@ enum plugin_status plugin_start(const void* file)
rb->audio_set_output_source(AUDIO_SRC_PLAYBACK);
#endif
rb->mixer_set_frequency(SAMPR_44);
rb->pcmbuf_fade(false, true); /* Be sure channel is audible */
if(file)
{

View file

@ -8,7 +8,7 @@ libmpeg2/slice.c
libmpeg2/idct_coldfire.S
libmpeg2/motion_comp_coldfire_c.c
libmpeg2/motion_comp_coldfire_s.S
#elif defined CPU_ARM
#elif defined CPU_ARM_CLASSIC
#if ARM_ARCH >= 6
libmpeg2/idct_armv6.S
#else

View file

@ -33,7 +33,7 @@
#include "attributes.h"
#include "mpeg2_internal.h"
#if defined(CPU_COLDFIRE) || defined (CPU_ARM)
#if defined(CPU_COLDFIRE) || defined (CPU_ARM_CLASSIC)
#define IDCT_ASM
#endif

View file

@ -31,6 +31,14 @@
* with the parameters previously supplied
*/
#if defined(DEBUG) || defined(SIMULATOR)
#define logf(...) rb->debugf(__VA_ARGS__); rb->debugf("\n")
#elif defined(ROCKBOX_HAS_LOGF)
#define logf rb->logf
#else
#define logf(...) do { } while(0)
#endif
#include "plugin.h"
#include "lang_enum.h"
#include "../open_plugin.h"
@ -46,16 +54,77 @@
#define MENU_ID_MAIN "0"
#define MENU_ID_EDIT "1"
static int fd_dat;
#define MAX_ENTRIES 128
static int fd_dat = -1;
static struct gui_synclist lists;
struct open_plugin_entry_t op_entry;
static struct open_plugin_entry_t op_entry;
static const uint32_t open_plugin_csum = OPEN_PLUGIN_CHECKSUM;
static const off_t op_entry_sz = sizeof(struct open_plugin_entry_t);
/* we only need the names for the first menu so don't bother reading paths yet */
const off_t op_name_sz = OPEN_PLUGIN_NAMESZ + (op_entry.name - (char*)&op_entry);
/*static char op_names[MAX_ENTRIES][OPEN_PLUGIN_NAMESZ + 1] = {0};*/
/* Forward declarations*/
static uint32_t op_entry_add_path(const char *key, const char *plugin, const char *parameter, bool use_key);
static int import_from_config(const char* cfgfile);
static int export_to_config(const char* cfgfile);
static bool hash_exists(uint32_t hash, bool remove)
{
/* keeps track of hash entries
* supply hash = 0, remove = true to erase all entries
* supply hash = 0, remove = false; returns true if empty entries are available
* supply a hash, remove = false; returns false if the hash does not exist & adds hash to the db
* supply a hash, remove = true; returns true and removes the hash from the db
* returns false if hash is unique
*/
static uint32_t seen_hashes[MAX_ENTRIES] = {0};
int i;
if (hash == 0)
{
if (remove)
{
logf("removing all hashes");
for(i = 0; i < MAX_ENTRIES; i++)
seen_hashes[i] = 0;
}
return seen_hashes[MAX_ENTRIES - 1] == 0; /* true if empty entries exist */
}
for(i = 0; i < MAX_ENTRIES; i++)
{
uint32_t current_hash = seen_hashes[i];
if (current_hash == 0 && !remove)
{
logf("(%d) added new hash: %x", i, hash);
seen_hashes[i] = hash;
break;
}
if (current_hash == hash)
{
if (remove)
{
logf("(%d) remove hash: %x", i, hash);
for (int j = i + 1; j < MAX_ENTRIES; j++, i++)
{
/* move everything above down 1*/
uint32_t next_hash = seen_hashes[j];
seen_hashes[i] = next_hash;
if (next_hash == 0)
break;
}
if (i + 1 == MAX_ENTRIES) /* handle last entry */
{
seen_hashes[i] = 0;
}
}
return true;
}
}
return false; /* hash is unique */
}
static bool _yesno_pop(const char* text)
{
@ -87,6 +156,7 @@ static size_t pathbasename(const char *name, const char **nameptr)
*nameptr = q;
return r - q;
}
static int op_entry_checksum(void)
{
if (op_entry.checksum != open_plugin_csum +
@ -134,7 +204,36 @@ static int op_entry_read_opx(const char *path)
return ret;
}
static void op_entry_export(int selection)
static void op_entry_config_export(void)
{
int i = 0;
int len;
char buf[MAX_PATH + 1];
do
{
i++;
rb->snprintf(buf, sizeof(buf), "%s/%s%d%s", ROCKBOX_DIR, "open_plugins", i, ".cfg");
} while (i < 100 && rb->file_exists(buf));
if( rb->kbd_input(buf, MAX_PATH, NULL ) == 0 )
{
len = rb->strlen(buf);
if(len > 4 && buf[len] != PATH_SEPCH &&
rb->strcasecmp(&((buf)[len-4]), ".cfg") != 0)
{
rb->strcat(buf, ".cfg");
}
if (export_to_config(buf) > 0)
return;
}
rb->splashf( 2*HZ, "Save Failed (%s)", buf );
}
static void op_entry_export_opx(int selection)
{
int len;
int fd = -1;
@ -257,19 +356,43 @@ static void op_entry_set_param(void)
rb->strlcpy(op_entry.param, tmp_buf, OPEN_PLUGIN_BUFSZ);
}
static int op_et_exclude_invalid(struct open_plugin_entry_t *op_entry, int item, void *data)
{
(void)item;
(void)data;
if (op_entry->hash == 0 || op_entry->name[0] == '\0')
{
logf("Exclude entry %d - Bad hash", item);
return 0;
}
if (op_entry->checksum != open_plugin_csum +
(op_entry->lang_id <= OPEN_PLUGIN_LANG_INVALID ? 0 : LANG_LAST_INDEX_IN_ARRAY))
{
logf("Exclude entry %d - Bad csum", item);
return 0;
}
if (!rb->file_exists(op_entry->path))
{
logf("Exclude entry %d - Bad path '%s'", item, op_entry->path);
return 0;
}
logf("%s Include entry %d %s - hash %x", __func__, item,
(op_entry->lang_id >= 0 ? "[Builtin]" : "[User]"), op_entry->hash);
return 1;
}
static int op_et_exclude_hash(struct open_plugin_entry_t *op_entry, int item, void *data)
{
(void)item;
if (op_entry->hash == 0 || op_entry->name[0] == '\0')
return 0;
if (data)
{
uint32_t *hash = data;
if (op_entry->hash != *hash)
return 1;
return op_et_exclude_invalid(op_entry, item, data);
}
logf("Exclude entry %d - hash %x", item, op_entry->hash);
return 0;
}
@ -279,11 +402,12 @@ static int op_et_exclude_builtin(struct open_plugin_entry_t *op_entry, int item,
(void)data;
if (op_entry->lang_id >= 0)
{
logf("Exclude entry %d - Builtin", item);
return 0;
else if(op_entry->hash == 0 || op_entry->name[0] == '\0')
return 0;
}
return 1;
return op_et_exclude_invalid(op_entry, item, data);
}
static int op_et_exclude_user(struct open_plugin_entry_t *op_entry, int item, void *data)
@ -292,11 +416,11 @@ static int op_et_exclude_user(struct open_plugin_entry_t *op_entry, int item, vo
(void)data;
if (op_entry->lang_id < 0)
{
logf("Exclude entry %d - User", item);
return 0;
else if (op_entry->hash == 0 || op_entry->name[0] == '\0')
return 0;
return 1;
}
return op_et_exclude_invalid(op_entry, item, data);
}
static int op_entry_transfer(int fd, int fd_tmp,
@ -304,21 +428,82 @@ static int op_entry_transfer(int fd, int fd_tmp,
void *data)
{
int entries = -1;
int skipped = 0;
if (fd_tmp >= 0 && fd >= 0 && rb->lseek(fd, 0, SEEK_SET) == 0)
{
entries = 0;
while (rb->read(fd, &op_entry, op_entry_sz) == op_entry_sz)
{
if (compfn && compfn(&op_entry, entries, data) > 0 && op_entry_checksum() > 0)
if (compfn && compfn(&op_entry, entries + skipped, data) > 0 && op_entry_checksum() > 0)
{
rb->write(fd_tmp, &op_entry, op_entry_sz);
entries++;
}
else
skipped++;
}
logf("%s %d entries %d skipped", __func__, entries, skipped);
}
else
{
logf("%s Error: fd %d, fd_tmp %d, seekfd: %d", __func__,
fd, fd_tmp, (int) rb->lseek(fd, 0, SEEK_SET));
}
return entries + 1;
}
#if 0
static int op_entry_exists(int fd)
{
logf("%s? %s - %s", __func__, op_entry.name, op_entry.path);
/* returns index of the entry that already exists if not found returns -1 */
static struct open_plugin_entry_t op_entry_rd;
int entries = 0;
if (fd >= 0 && rb->lseek(fd, 0, SEEK_SET) == 0)
{
while (rb->read(fd, &op_entry_rd, op_entry_sz) == op_entry_sz)
{
logf("rd: %s - %s", op_entry_rd.name, op_entry_rd.path);
if (rb->memcmp(&op_entry_rd, &op_entry, op_entry_sz) == 0)
{
logf("%s entry exists @ %d", op_entry_rd.name, entries);
return entries;
}
entries++;
}
}
logf("%s is unique in %d entries", op_entry.name, entries);
return -1;
}
#endif
static bool browse_configs(void)
{
int entries = 0;
char tmp_buf[MAX_PATH+1];
struct browse_context browse = {
.dirfilter = SHOW_CFG,
.flags = BROWSE_SELECTONLY | BROWSE_DIRFILTER,
.title = rb->str(LANG_CUSTOM_CFG),
.icon = Icon_Plugin,
.root = ROCKBOX_DIR,
.buf = tmp_buf,
.bufsize = sizeof(tmp_buf),
};
if (rb->rockbox_browse(&browse) == GO_TO_PREVIOUS)
{
logf("import from %s\n", tmp_buf);
if(rb->filetype_get_attr(tmp_buf) == FILE_ATTR_CFG)
{
entries = import_from_config(tmp_buf);
rb->splashf(HZ *2, "Imported %d entries", entries);
}
}
return (entries > 0);
}
static uint32_t op_entry_add_path(const char *key, const char *plugin, const char *parameter, bool use_key)
{
char buf[MAX_PATH];
@ -358,8 +543,6 @@ static uint32_t op_entry_add_path(const char *key, const char *plugin, const cha
if (op_entry.name[0] == '\0' || op_entry.lang_id >= 0)
rb->strlcpy(op_entry.name, pos, OPEN_PLUGIN_NAMESZ);
if ((!parameter || parameter[0] == '\0') && fattr != FILE_ATTR_ROCK && fattr != FILE_ATTR_OPX)
{
rb->strlcpy(op_entry.param, plugin, OPEN_PLUGIN_BUFSZ);
@ -396,16 +579,28 @@ static uint32_t op_entry_add_path(const char *key, const char *plugin, const cha
}
else if (parameter != op_entry.param)
rb->strlcpy(op_entry.param, parameter, OPEN_PLUGIN_BUFSZ);
}
/* hash on the parameter path if it is a file */
if (op_entry.lang_id <0 && (key == op_entry.path || key == NULL) &&
rb->file_exists(op_entry.param))
op_entry_set_checksum();
if (!use_key)
{
if (op_entry.lang_id <0)
{
open_plugin_get_hash(op_entry.param, &newhash);
op_entry.hash = newhash;
open_plugin_get_hash(op_entry.name, &hash);
op_entry.hash = hash;
}
if (hash_exists(op_entry.hash, false) &&
_yesno_pop(ID2P(LANG_REALLY_OVERWRITE)) == false)
{
logf("%s error: duplicate key exists: '%s' lang id: %d hash: %x",
__func__, key, op_entry.lang_id, op_entry.hash);
rb->close(fd_tmp);
rb->remove(OPEN_PLUGIN_DAT ".tmp");
return op_entry.hash;
}
}
op_entry_set_checksum();
rb->write(fd_tmp, &op_entry, op_entry_sz); /* add new entry first */
}
else if(op_entry_read_opx(plugin) == op_entry_sz)
@ -414,13 +609,23 @@ static uint32_t op_entry_add_path(const char *key, const char *plugin, const cha
if (fd_tmp < 0)
return 0;
if (op_entry.lang_id <0 && rb->file_exists(op_entry.param))
open_plugin_get_hash(op_entry.param, &hash);
else
open_plugin_get_hash(op_entry.path, &hash);
if (op_entry.lang_id <0)
{
open_plugin_get_hash(op_entry.name, &hash);
}
op_entry.hash = hash;
op_entry_set_checksum();
if (hash_exists(op_entry.hash, false))
{
logf("%s error: duplicate key exists: '%s' lang id: %d hash: %x",
__func__, key, op_entry.lang_id, op_entry.hash);
rb->close(fd_tmp);
rb->remove(OPEN_PLUGIN_DAT ".tmp");
return op_entry.hash;
}
rb->write(fd_tmp, &op_entry, op_entry_sz); /* add new entry first */
}
else
@ -431,6 +636,7 @@ static uint32_t op_entry_add_path(const char *key, const char *plugin, const cha
}
}
/*logf("OP add key: '%s' lang id: %d hash: %x", op_entry.name, op_entry.lang_id, op_entry.hash);*/
if (op_entry_transfer(fd_dat, fd_tmp, op_et_exclude_hash, &hash) > 0)
{
rb->close(fd_tmp);
@ -451,6 +657,7 @@ static uint32_t op_entry_add_path(const char *key, const char *plugin, const cha
void op_entry_browse_add(int selection)
{
logf("%s", __func__);
char* key;
op_entry_read(fd_dat, selection, op_entry_sz);
if (op_entry_set_path() > 0)
@ -466,7 +673,6 @@ void op_entry_browse_add(int selection)
static void op_entry_remove(int selection)
{
int entries = rb->lseek(fd_dat, 0, SEEK_END) / op_entry_sz;
int32_t hash = 0;
int lang_id = -1;
@ -486,6 +692,7 @@ static void op_entry_remove(int selection)
rb->global_settings->start_in_screen = GO_TO_ROOT + 2; /* default */
}
}
hash_exists(op_entry.hash, true);
rb->memset(&op_entry, 0, op_entry_sz);
op_entry.lang_id = lang_id;
op_entry.hash = hash;
@ -685,9 +892,9 @@ static int context_menu_cb(int action,
/*Run, Edit, Remove, Export, Blank, Import, Add, Back*/
switch(selection)
{
case 0:case 1:case 2:case 3:case 5:
case 0:case 1:case 2:case 3:case 4:case 5:case 7:
return ACTION_STD_OK;
case 4: /*blank*/
case 6: /*blank*/
break;
default:
return ACTION_STD_CANCEL;
@ -709,6 +916,9 @@ static void edit_menu(int selection)
if (!op_entry_read(fd_dat, selection, op_entry_sz))
return;
if (op_entry.hash != 0) /* remove old hash */
hash_exists(op_entry.hash, true);
uint32_t crc = rb->crc_32(&op_entry, op_entry_sz, 0xffffffff);
synclist_set(MENU_ID_EDIT, 2, 8, 2);
@ -762,6 +972,8 @@ static void edit_menu(int selection)
op_entry_add_path(NULL, op_entry.path, param, false);
fd_dat = rb->open(OPEN_PLUGIN_DAT, O_RDWR, 0666);
}
else
hash_exists(op_entry.hash, false);
}
static int context_menu(int selection)
@ -771,9 +983,11 @@ static int context_menu(int selection)
{
MENUITEM_STRINGLIST(menu, op_entry.name, context_menu_cb,
ID2P(LANG_RUN), ID2P(LANG_EDIT), ID2P(LANG_REMOVE), ID2P(LANG_EXPORT),
ID2P(VOICE_BLANK), ID2P(LANG_ADD), ID2P(LANG_BACK));
ID2P(LANG_SAVE_SETTINGS), ID2P(LANG_CUSTOM_CFG), ID2P(VOICE_BLANK),
ID2P(LANG_ADD), ID2P(LANG_BACK));
selected_item = rb->do_menu(&menu, 0, NULL, false);
logf("%s %d", __func__, selected_item);
switch (selected_item)
{
case 0: /*run*/
@ -785,11 +999,20 @@ static int context_menu(int selection)
op_entry_remove(selection);
break;
case 3: /*export*/
op_entry_export(selection);
op_entry_export_opx(selection);
break;
case 4: /*blank*/
case 4: /* export to cfg */
op_entry_config_export();
case 5: /*import from cfg*/
if (browse_configs())
{
rb->plugin_open(rb->plugin_get_current_filename(), "\0");
return OP_PLUGIN_RESTART;
}
break;
case 5: /*add*/
case 6: /*blank*/
break;
case 7: /*add*/
op_entry_browse_add(-1);
rb->plugin_open(rb->plugin_get_current_filename(), "\0");
return OP_PLUGIN_RESTART;
@ -802,6 +1025,296 @@ static int context_menu(int selection)
return PLUGIN_ERROR;
}
/* Read up to buffer_size chars from a quoted string
* within fd into buffer and return number of bytes read.
* A string starts with a quote character (single or double quote)
* and ends with a matching closing quote. Neither opening or closing quotes
* are stored in buffer. Too small buf or no opening and closing quote is an error.
* If an error occurs, -1 is returned (and buffer is cleared).
* If buffer too small file will still be advanced to the closing quote/LF/EOF
*/
static int read_quoted_string(int fd, char* buffer, int buffer_size)
{
int pos = 0;
char ch;
char quote = '\0';
/*logf("%s fd: %d bufsz: %d", __func__, fd, buffer_size);*/
while (rb->read(fd, &ch, 1) == 1)
{
if (ch == '\n') /*LF marks end of line*/
{
rb->lseek(fd, -1, SEEK_CUR);/*back up cursor to LF so calling fn sees it*/
break; /* fail */
}
if (quote == '\0' &&
(ch == '\'' || ch == '"')) /*handle single or double quotes*/
{
quote = ch;
}
else if (quote != '\0')
{
if (ch == quote)
{
if (pos < buffer_size)
{
buffer[pos] = '\0'; /*end quote*/
return pos + 1;
}
break; /*fail*/
}
if (pos < buffer_size)
{
buffer[pos] = ch; /*inside quote*/
pos++;
}
}
}
/*fail*/
/*logf("Error %s", __func__);*/
buffer[0] = '\0';
return -1;
}
static int lang_english_to_id(const char *english)
{
int i;
unsigned char *ptr;
size_t ptrlen, len = rb->strlen(english);
for (i = 0; i < LANG_LAST_INDEX_IN_ARRAY; i++) {
ptr = rb->language_strings[i];
ptrlen = rb->strlen((char *)ptr);
if ((ptrlen == len) && rb->memcmp(ptr, english, ptrlen) == 0)
return i;
}
return -1;
}
static bool op_entry_config_import(int cfg_fd, int fd_tmp)
{
/* NOTE: assumes cfg_fd is valid */
/*"key", "name", "path", "param"*/
/*"Start Screen", "logo.rock", "/.rockbox/rocks/demos/logo.rock", ""*/
/*"[USER]", "text_viewer.rock", "/.rockbox/rocks/viewers/text_viewer.rock", "/text.txt"*/
rb->memset(&op_entry, 0, op_entry_sz);
static char errmsg[MAX_PATH];
static char keybuf[MAX_PATH];
int32_t lang_id;
uint32_t hash;
if (read_quoted_string(cfg_fd, keybuf, sizeof(keybuf)) < 0)
{
logf("%s error: importing key entry @ %d", __func__, 0);
rb->snprintf(errmsg, sizeof(errmsg), "importing key entry @ %d", 1);
rb->splashf(HZ*2, ID2P(LANG_ERROR_FORMATSTR), errmsg);
return false; /* fail */
}
lang_id = lang_english_to_id(keybuf);
if (lang_id < 0)
{
int rd = read_quoted_string(cfg_fd, keybuf, sizeof(keybuf)); /* grab name field */
if(rd < 0)
{
logf("%s error: importing key entry @ %d", __func__, 1);
rb->snprintf(errmsg, sizeof(errmsg), "importing key entry @ %d", 1);
rb->splashf(HZ*2, ID2P(LANG_ERROR_FORMATSTR), errmsg);
return false; /* fail */
}
rb->lseek(cfg_fd, -(rd+2), SEEK_CUR); /* restore position to read name again */
}
int i, bufsz;
char *field[3] = {op_entry.name, op_entry.path, op_entry.param};
for (i = 0, bufsz = OPEN_PLUGIN_NAMESZ; i < (int)ARRAYLEN(field); i++)
{
if (read_quoted_string(cfg_fd, field[i], bufsz) < 0)
{
logf("%s error: importing entry @ %d", __func__, i);
logf("OP import key: '%s' name: '%s' '%s' '%s'", keybuf,
op_entry.name, op_entry.path, op_entry.param);
rb->memset(&op_entry, 0, op_entry_sz);
rb->snprintf(errmsg, sizeof(errmsg), "importing entry %s @ %d", keybuf, i);
rb->splashf(HZ*2, ID2P(LANG_ERROR_FORMATSTR), errmsg);
return false; /* fail */
}
bufsz = OPEN_PLUGIN_BUFSZ;
}
if (!rb->file_exists(op_entry.path))
{
logf("%s error: '%s' '%s' does not exist", __func__, keybuf, op_entry.path);
rb->splashf(HZ*2, ID2P(LANG_PLUGIN_CANT_OPEN), op_entry.path);
return false; /* fail */
}
if (lang_id <0)
{
open_plugin_get_hash(op_entry.name, &hash);
}
else
{
open_plugin_get_hash(keybuf, &hash);
}
op_entry.hash = hash;
op_entry.lang_id = lang_id;
/*logf("OP import key: '%s' name: '%s' '%s' '%s'", keybuf,
op_entry.name, op_entry.path, op_entry.param);*/
op_entry_set_checksum();
if (fd_tmp >= 0 && fd_dat >= 0)
{
if (hash_exists(op_entry.hash, false))
{
logf("%s error: duplicate key exists: '%s' lang id: %d hash: %x",
__func__, keybuf, lang_id, hash);
return false;
}
logf("writing to tmp: %s - %s", op_entry.name, op_entry.path);
if (rb->write(fd_tmp, &op_entry, op_entry_sz) != op_entry_sz)/* add new entry */
return false;
}
else
{
logf("%s error: bad fd dat: %d tmp: %d", __func__, fd_dat, fd_tmp);
return false;
}
return true;
}
static int import_from_config(const char* cfgfile)
{
logf("%s() %s\r\n", __func__, cfgfile);
int fd;
char ch;
int entries = 0;
static char line[sizeof("openplugin:")];
fd = rb->open_utf8(cfgfile, O_RDONLY);
if (fd < 0)
return -1;
int fd_tmp = rb->open(OPEN_PLUGIN_DAT ".tmp", O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (fd_tmp < 0)
{
logf("%s error: can not open '%s'", __func__, OPEN_PLUGIN_DAT ".tmp");
return -1;
}
while ((rb->read(fd, line, sizeof line) - 1) == sizeof(line) - 1)
{
if (rb->strncasecmp(line, "openplugin:", sizeof("openplugin:") -1) == 0)
{
if (op_entry_config_import(fd, fd_tmp))
entries++;
}
while (rb->read(fd, &ch, 1) == 1) /* continue reading till EOL */
{
if (ch == '\n')
break;
}
} /* while(...) */
rb->close(fd);
#if 1
if (entries > 0 && op_entry_transfer(fd_dat, fd_tmp, &op_et_exclude_user, NULL) > 0 &&
op_entry_transfer(fd_dat, fd_tmp, &op_et_exclude_builtin, NULL) > 0)
#else
if (entries > 0 && op_entry_transfer(fd_dat, fd_tmp, op_et_exclude_invalid, 0) >= 0)
#endif
{
logf("%s imported %d entries", __func__, entries);
rb->close(fd_tmp);
rb->close(fd_dat);
fd_dat = -1;
rb->remove(OPEN_PLUGIN_DAT);
rb->rename(OPEN_PLUGIN_DAT ".tmp", OPEN_PLUGIN_DAT);
}
else
{
logf("%s error: can not transfer entries", __func__);
if (entries > 0)
{
logf("%s error: can not transfer entries to '%s'", __func__, OPEN_PLUGIN_DAT ".tmp");
entries = -1;
}
rb->close(fd_tmp);
rb->remove(OPEN_PLUGIN_DAT ".tmp");
}
return entries;
}
static int export_to_config(const char* cfgfile)
{
logf("%s() %s\r\n", __func__, cfgfile);
int cfg_fd;
char *lang_name;
int fd;
int index = 0;
cfg_fd = rb->open_utf8(cfgfile, O_WRONLY | O_CREAT | O_TRUNC);
if (cfg_fd < 0)
return index;
fd = rb->open_utf8(OPEN_PLUGIN_DAT, O_RDONLY);
if (fd < 0)
{
logf("%s error: opening %s", __func__, OPEN_PLUGIN_DAT);
rb->close(cfg_fd);
rb->remove(cfgfile);
return index; /* OPEN_PLUGIN_NOT_FOUND */
}
while (op_entry_read(fd, index, op_entry_sz))
{
index++;
/* don't save the LANG_OPEN_PLUGIN entry -- it is for internal use */
if (op_entry.lang_id == LANG_OPEN_PLUGIN)
{
continue;
}
if (op_entry.lang_id >=0)
lang_name = rb->str(op_entry.lang_id);
else
lang_name = "[USER]"; /* needs to be an invalid lang string */
bool dblquote = rb->strchr(op_entry.name, '"') != NULL ||
rb->strchr(op_entry.param, '"') != NULL;
const char* fmtstr = "%s: \"%s\", \"%s\", \"%s\", \"%s\"\n";
if (dblquote) /* if using double quotes export with single quotes */
{
fmtstr = "%s: '%s', '%s', '%s', '%s'\n";
}
rb->fdprintf(cfg_fd,fmtstr, OPEN_PLUGIN_CFGNAME,
lang_name, op_entry.name, op_entry.path, op_entry.param);
logf("openplugin[%d]: \"%s\", \"%s\", \"%s\", \"%s\"\n lang id: %d hash: %x, csum: %x\n",
index, lang_name, op_entry.name, op_entry.path, op_entry.param,
op_entry.lang_id, op_entry.hash, op_entry.checksum);
}
rb->close(fd);
rb->close(cfg_fd);
logf("%s exported %d entries", __func__, index);
if (index == 0)
{
/* Nothing to export */
rb->remove(cfgfile);
}
return index;
}
enum plugin_status plugin_start(const void* parameter)
{
int ret = PLUGIN_OK;
@ -817,11 +1330,20 @@ enum plugin_status plugin_start(const void* parameter)
const int creat_flags = O_RDWR | O_CREAT;
reopen_datfile:
fd_dat = rb->open(OPEN_PLUGIN_DAT, creat_flags, 0666);
hash_exists(0, true);
while (rb->read(fd_dat, &op_entry, op_entry_sz) == op_entry_sz)
{
hash_exists(op_entry.hash, false);
}
if (!fd_dat)
exit = true;
items = rb->lseek(fd_dat, 0, SEEK_END) / op_entry_sz;
if (parameter)
{
path = (char*)parameter;
@ -833,13 +1355,18 @@ reopen_datfile:
parameter = NULL;
op_entry_browse_add(-1);
rb->close(fd_dat);
fd_dat = -1;
goto reopen_datfile;
}
}
if(rb->filetype_get_attr(path) == FILE_ATTR_CFG)
{
int ret = import_from_config(path);
rb->close(fd_dat);
if (ret >= 0)
return PLUGIN_OK;
return PLUGIN_ERROR;
}
if (parameter)
{
path = (char*)parameter;
res = op_entry_read_opx(path);
if (res >= 0)
{
@ -910,6 +1437,7 @@ reopen_datfile:
if (op_entry_add_path(rb->str(LANG_ADD), cur_filename, "-add", true))
{
rb->close(fd_dat);
fd_dat = -1;
parameter = NULL;
goto reopen_datfile;
}
@ -917,8 +1445,6 @@ reopen_datfile:
return PLUGIN_ERROR;
}
if (!exit)
{
synclist_set(MENU_ID_MAIN, selection, items, 1);
@ -947,7 +1473,7 @@ reopen_datfile:
rb->gui_synclist_draw(&lists);
break;
}
/* Inentional fallthrough */
/* fallthrough */
case ACTION_STD_OK:
if (op_entry_read(fd_dat, selection, op_entry_sz))
{
@ -962,6 +1488,14 @@ reopen_datfile:
exit = true;
break;
}
default:
if (rb->default_event_handler(action) == SYS_USB_CONNECTED)
{
op_entry_remove_empty();
rb->close(fd_dat);
return PLUGIN_USB_CONNECTED;
}
break;
}
}
op_entry_remove_empty();

View file

@ -411,6 +411,7 @@ static void start_sound(void)
.get_more = get_more,
};
rb->mixer_set_frequency(caps->samprs[sr_index]);
rb->pcmbuf_fade(false, true); /* Be sure channel is audible */
rb->mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK, &cbs, NULL, 0);
sound_playing = true;
@ -424,6 +425,7 @@ static void stop_sound(void)
if (!sound_playing)
return;
rb->pcmbuf_fade(false, false); /* Mute channel */
rb->mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK);
rb->mixer_set_frequency(HW_SAMPR_DEFAULT);

View file

@ -65,6 +65,9 @@ void rockbox_open_audio(int rate)
/* Set sample rate of the audio buffer. */
rb->mixer_set_frequency(rate);
/* Be sure channel is audible */
rb->pcmbuf_fade(false, true);
/* Initialize output buffer. */
for(i = 0; i < OUTBUFSIZE; i++)
outbuf[i].fill = 0;
@ -77,6 +80,9 @@ void rockbox_open_audio(int rate)
/* Close audio. */
void rockbox_close_audio(void)
{
/* Mute channel */
rb->pcmbuf_fade(false, false);
/* Stop playback. */
rb->mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK);

View file

@ -1106,9 +1106,9 @@ int gui_syncpitchscreen_run(void)
/* when needed */
new_speed = 0;
}
rb->yield();
}
//rb->pcmbuf_set_low_latency(false);
//pop_current_activity();
/* Clean up */
@ -1215,6 +1215,7 @@ enum plugin_status plugin_start(const void* parameter)
* -s=90 sets speed to 90% if timestrech is enabled
* -k=true -k1 enables time stretch -k0 -kf-kn disables
*/
enum plugin_status ret = PLUGIN_OK;
bool gui = false;
rb->pcmbuf_set_low_latency(true);
@ -1274,7 +1275,7 @@ enum plugin_status plugin_start(const void* parameter)
}
if (gui && gui_syncpitchscreen_run() == 1)
return PLUGIN_USB_CONNECTED;
ret = PLUGIN_USB_CONNECTED;
rb->pcmbuf_set_low_latency(false);
return PLUGIN_OK;
return ret;
}

View file

@ -187,7 +187,8 @@ static int browse_file_or_dir(struct dir_stats *stats)
continue;
switch(button)
{
case ACTION_STD_OK:;
case ACTION_STD_OK:
rb->gui_synclist_scroll_stop(&properties_lists);
int sel_pos = rb->gui_synclist_get_sel_pos(&properties_lists);
/* "Show Track Info..." selected? */
@ -197,17 +198,10 @@ static int browse_file_or_dir(struct dir_stats *stats)
return -1;
else
{
const unsigned char* const *props = (props_type == PROPS_DIR) ?
props_dir : props_file;
/* Display field in fullscreen */
FOR_NB_SCREENS(i)
rb->viewportmanager_theme_enable(i, false, NULL);
if (props_type == PROPS_DIR)
view_text((char *) p2str(props_dir[sel_pos]),
(char *) props_dir[sel_pos + 1]);
else
view_text((char *) p2str(props_file[sel_pos]),
(char *) props_file[sel_pos + 1]);
FOR_NB_SCREENS(i)
rb->viewportmanager_theme_undo(i, false);
view_text((char *) p2str(props[sel_pos]), props[sel_pos + 1]);
rb->gui_synclist_set_title(&properties_lists,
rb->str(props_type == PROPS_DIR ?
@ -335,6 +329,8 @@ enum plugin_status plugin_start(const void* parameter)
{
static struct dir_stats stats;
const char *file = parameter;
static struct viewport ui_vp;
#ifdef HAVE_TOUCHSCREEN
rb->touchscreen_set_mode(rb->global_settings->touch_mode);
#endif
@ -346,6 +342,17 @@ enum plugin_status plugin_start(const void* parameter)
return PLUGIN_OK;
}
/* erase background behind progress bar to prevent glitches
for themes adjusting viewport for context menu activity */
if (props_type != PROPS_DIR)
{
struct screen* display = rb->screens[SCREEN_MAIN];
rb->viewport_set_defaults(&ui_vp, SCREEN_MAIN);
struct viewport *last_vp = display->set_viewport(&ui_vp);
display->clear_viewport();
display->set_viewport(last_vp);
}
if (props_type == PROPS_MUL_ID3)
ret = assemble_track_info(NULL, NULL);
else if (props_type != PROPS_ID3)

View file

@ -3066,6 +3066,7 @@ static void tune_input(const char *name)
"Signpost",
"Slide",
"Untangle",
NULL
};
input_settings.sticky_mouse = string_in_list(name, sticky_mouse_games);

View file

@ -56,12 +56,14 @@ void rockboy_pcm_init(void)
#endif
rb->mixer_set_frequency(pcm.hz); /* 44100 22050 11025 */
rb->pcmbuf_fade(false, true); /* Be sure channel is audible */
}
void rockboy_pcm_close(void)
{
memset(&pcm, 0, sizeof pcm);
newly_started = true;
rb->pcmbuf_fade(false, false); /* Mute channel */
rb->mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK);
rb->mixer_set_frequency(HW_SAMPR_DEFAULT);
}

View file

@ -19,7 +19,7 @@ ifeq ($(findstring YES, $(call preprocess, $(APPSDIR)/plugins/BUILD_OVERLAY)), Y
## lowmem targets
ROCKS += $(ROCKBOY_OBJDIR)/rockboy.ovl
ROCKBOY_OUTLDS = $(ROCKBOY_OBJDIR)/rockboy.link
ROCKBOY_OVLFLAGS = -T$(ROCKBOY_OUTLDS) -Wl,--gc-sections -Wl,-Map,$(basename $@).map
ROCKBOY_OVLFLAGS = -T$(ROCKBOY_OUTLDS) -Wl,--gc-sections -Wl,-Map,$(basename $@).map $(GLOBAL_LDOPTS)
else
ROCKS += $(ROCKBOY_OBJDIR)/rockboy.rock
endif

View file

@ -286,7 +286,7 @@ static SDL_RWops *LoadWAVStream (SDL_RWops *src, SDL_AudioSpec *spec,
/* WAV magic header */
Uint32 RIFFchunk;
// Uint32 wavelen;
Uint32 wavelen;
Uint32 WAVEmagic;
/* FMT chunk */
@ -296,8 +296,12 @@ static SDL_RWops *LoadWAVStream (SDL_RWops *src, SDL_AudioSpec *spec,
/* Check the magic header */
RIFFchunk = SDL_ReadLE32(src);
// wavelen = SDL_ReadLE32(src);
wavelen = SDL_ReadLE32(src);
WAVEmagic = SDL_ReadLE32(src);
/* Unused */
(void)wavelen;
if ( (RIFFchunk != RIFF) || (WAVEmagic != WAVE) ) {
Mix_SetError("Unrecognized file type (not WAVE)");
was_error = 1;

View file

@ -548,7 +548,7 @@ void Draw_ConsoleBackground (int lines)
conback = Draw_CachePic ("gfx/conback.lmp");
dest = conback->data + 320 - 43 + 320*186;
sprintf (ver, "%.2f", (float)VERSION);
snprintf (ver, sizeof(ver), "%.2f", (float)VERSION);
for (x=0 ; x<strlen(ver) ; x++)
Draw_CharToConback (ver[x], dest+(x<<3));

View file

@ -78,7 +78,7 @@ $(SDL_OBJDIR)/duke3d.ovl: $(SDL_OBJ) $(DUKE3D_OBJ) $(TLSFLIB) $(DUKE3D_OUTLDS)
$(filter %.o, $^) \
$(filter %.a, $+) \
-lgcc -T$(DUKE3D_OUTLDS) $(SDL_OVLFLAGS)
$(call PRINTS,LD $(@F))$(call objcopy,$(basename $@).elf,$@)
$(call PRINTS,LD $(@F))$(call objcopy_plugin,$(basename $@).elf,$@)
# Wolf3D

View file

@ -215,6 +215,7 @@ static Uint8 *ROCKBOXAUD_GetAudioBuf(_THIS)
static void ROCKBOXAUD_CloseAudio(_THIS)
{
rb->pcmbuf_fade(false, false); /* Mute channel */
rb->mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK);
if ( this->hidden->mixbuf != NULL ) {
SDL_FreeAudioMem(this->hidden->mixbuf);
@ -260,6 +261,7 @@ static int ROCKBOXAUD_OpenAudio(_THIS, SDL_AudioSpec *spec)
LOGF("samplerate %d", spec->freq);
rb->mixer_set_frequency(spec->freq);
rb->pcmbuf_fade(false, true); /* Be sure channel is audible */
/* Allocate mixing buffer */
this->hidden->mixlen = spec->size;

View file

@ -214,6 +214,7 @@ static void play_tone(bool volume_set)
#if INPUT_SRC_CAPS != 0
/* Select playback */
rb->audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK);
rb->audio_set_output_source(AUDIO_SRC_PLAYBACK);
#endif
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
@ -221,11 +222,7 @@ static void play_tone(bool volume_set)
#endif
rb->mixer_set_frequency(hw_sampr);
#if INPUT_SRC_CAPS != 0
/* Recordable targets can play back from other sources */
rb->audio_set_output_source(AUDIO_SRC_PLAYBACK);
#endif
rb->pcmbuf_fade(false, true); /* Be sure channel is audible */
gen_quit = false;
output_clear();
@ -263,6 +260,7 @@ static void play_tone(bool volume_set)
rb->thread_wait(gen_thread_id);
rb->pcmbuf_fade(false, false); /* Mute channel */
rb->mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK);
#ifdef HAVE_ADJUSTABLE_CPU_FREQ

View file

@ -257,6 +257,9 @@ bool syssnd_init(void)
rb->mixer_set_frequency(HW_FREQ_44);
/* Be sure channel is audible */
rb->pcmbuf_fade(false, true);
rb->memset(channels, 0, sizeof(channels));
rb->memset(mixBuffers, 0, sizeof(mixBuffers));
@ -280,6 +283,9 @@ void syssnd_shutdown(void)
return;
}
/* Mute channel */
rb->pcmbuf_fade(false, false);
/* Stop playback. */
rb->mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK);

View file

@ -1032,12 +1032,14 @@ void sys_startAudio(struct System* sys, AudioCallback callback, void *param)
static const struct mixer_play_cbs cbs = {
.get_more = get_more,
};
rb->pcmbuf_fade(false, true); /* Be sure channel is audible */
rb->mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK, &cbs, NULL, 0);
}
void sys_stopAudio(struct System* sys)
{
(void) sys;
rb->pcmbuf_fade(false, false); /* Mute channel */
rb->mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK);
}

View file

@ -112,12 +112,14 @@ static void open_snd(void)
rb->audio_set_output_source(AUDIO_SRC_PLAYBACK);
#endif
rb->mixer_set_frequency(SAMPR_44);
rb->pcmbuf_fade(false, true); /* Be sure channel is audible */
}
static void close_snd(int normal)
{
(void)normal;
sound_avail = 0;
rb->pcmbuf_fade(false, false); /* Mute channel */
rb->mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK);
}

View file

@ -827,6 +827,7 @@ refresh_info:
{
if (key == ACTION_STD_OK)
{
gui_synclist_scroll_stop(&id3_lists);
int header_id = id3_headers[info.info_id[id3_lists.selected_item/2]];
char* title_and_text[2];
title_and_text[0] = str(header_id);
@ -835,13 +836,7 @@ refresh_info:
title_and_text[1] = (char*)id3_get_or_speak_info(id3_lists.selected_item+1,&info, buffer, sizeof(buffer), false);
if (view_text)
{
FOR_NB_SCREENS(i)
viewportmanager_theme_enable(i, false, NULL);
view_text(title_and_text[0], title_and_text[1]);
FOR_NB_SCREENS(i)
viewportmanager_theme_undo(i, false);
}
else
plugin_load(VIEWERS_DIR"/view_text.rock", title_and_text);
gui_synclist_set_title(&id3_lists, str(LANG_TRACK_INFO), NOICON);

View file

@ -405,7 +405,7 @@ bool settings_load_config(const char* file, bool apply)
int fd;
char line[128];
bool theme_changed = false;
bool import_open_plugins = false;
fd = open_utf8(file, O_RDONLY);
if (fd < 0)
return false;
@ -418,17 +418,25 @@ bool settings_load_config(const char* file, bool apply)
if (!string_to_cfg(name, value, &theme_changed))
{
#ifndef __PCTOOL__
/* if we are here then name was not a valid setting */
if (!strcmp(name, "openplugin"))
if (strcmp(name, OPEN_PLUGIN_CFGNAME) == 0)
{
open_plugin_import(value);
import_open_plugins = true;
char ch;
while (read(fd, &ch, 1) == 1 && ch != '\n'){};
}
#endif
}
} /* while(...) */
close(fd);
#ifndef __PCTOOL__
if (import_open_plugins)
open_plugin_import(file);
#else
(void) import_open_plugins;
#endif
if (apply)
{
settings_save();

View file

@ -67,6 +67,7 @@
#endif
#include "playlist.h"
#include "tree.h"
#include "iap-usb.h"
#include "voice_thread.h"
@ -790,15 +791,17 @@ static void shuffle_playlist_callback(bool shuffle)
}
}
}
iap_on_shuffle_state(shuffle);
}
static void repeat_mode_callback(int repeat)
{
(void)repeat;
if ((audio_status() & AUDIO_STATUS_PLAY) == AUDIO_STATUS_PLAY)
{
audio_flush_and_reload_tracks();
}
(void)repeat;
iap_on_repeat_state(repeat);
}
static void treesort_callback(int value)
@ -2337,30 +2340,38 @@ const struct settings_list settings[] = {
#endif
#ifdef HAVE_HOTKEY
/* WPS HOTKEY */
TABLE_SETTING(F_CB_ON_SELECT_ONLY, hotkey_wps,
LANG_HOTKEY_WPS, HOTKEY_VIEW_PLAYLIST, "hotkey wps",
"off,view playlist,show track info,pitchscreen,open with,delete,bookmark,plugin,bookmark list"
,UNIT_INT, hotkey_formatter, hotkey_getlang, hotkey_callback,9, HOTKEY_OFF,
HOTKEY_VIEW_PLAYLIST, HOTKEY_SHOW_TRACK_INFO, HOTKEY_PITCHSCREEN,
HOTKEY_OPEN_WITH, HOTKEY_DELETE, HOTKEY_BOOKMARK, HOTKEY_PLUGIN, HOTKEY_BOOKMARK_LIST),
#ifdef HAVE_ALBUMART
",show_album_art,context menu"
,UNIT_INT, hotkey_formatter, hotkey_getlang, hotkey_callback,11,
#else
",context menu"
,UNIT_INT, hotkey_formatter, hotkey_getlang, hotkey_callback,10,
#endif
HOTKEY_OFF, HOTKEY_VIEW_PLAYLIST, HOTKEY_SHOW_TRACK_INFO, HOTKEY_PITCHSCREEN,
HOTKEY_OPEN_WITH, HOTKEY_DELETE, HOTKEY_BOOKMARK, HOTKEY_PLUGIN, HOTKEY_BOOKMARK_LIST,
#ifdef HAVE_ALBUMART
HOTKEY_ALBUMART,
#endif
HOTKEY_CONTEXT_MENU),
/* TREE HOTKEY */
TABLE_SETTING(0, hotkey_tree,
LANG_HOTKEY_FILE_BROWSER, HOTKEY_OFF, "hotkey tree",
#ifdef HAVE_TAGCACHE
"off,properties,pictureflow,open with,delete,insert,insert shuffled",
"off,properties,pictureflow,open with,delete,insert,insert shuffled,context menu",
UNIT_INT, hotkey_formatter, hotkey_getlang, NULL, 8,
#else
"off,properties,open with,delete,insert,insert shuffled",
#endif
UNIT_INT, hotkey_formatter, hotkey_getlang, NULL,
#ifdef HAVE_TAGCACHE
7,
#else
6,
"off,properties,open with,delete,insert,insert shuffled,context menu",
UNIT_INT, hotkey_formatter, hotkey_getlang, NULL, 7,
#endif
HOTKEY_OFF,HOTKEY_PROPERTIES,
#ifdef HAVE_TAGCACHE
HOTKEY_PICTUREFLOW,
#endif
HOTKEY_OPEN_WITH, HOTKEY_DELETE, HOTKEY_INSERT, HOTKEY_INSERT_SHUFFLED),
HOTKEY_OPEN_WITH, HOTKEY_DELETE, HOTKEY_INSERT, HOTKEY_INSERT_SHUFFLED, HOTKEY_CONTEXT_MENU),
#endif /* HAVE_HOTKEY */
INT_SETTING(F_TIME_SETTING, resume_rewind, LANG_RESUME_REWIND, 0,

View file

@ -413,6 +413,9 @@ static int update_dir(void)
const bool id3db = false;
#endif
/* Ensure that list is initialized before update_dir returns */
gui_synclist_init(list, &tree_get_filename, &tc, false, 1, NULL);
#ifdef HAVE_TAGCACHE
/* Checks for changes */
if (id3db) {
@ -461,8 +464,6 @@ static int update_dir(void)
}
}
gui_synclist_init(list, &tree_get_filename, &tc, false, 1, NULL);
#ifdef HAVE_TAGCACHE
if (id3db)
{

View file

@ -957,6 +957,20 @@ usbstack/usb_charging_only.c
#ifdef USB_ENABLE_HID
usbstack/usb_hid.c
#endif
#ifdef USB_ENABLE_IAP
usbstack/iap/audio.c
usbstack/iap/buffer.c
usbstack/iap/debug.c
usbstack/iap/libiap/debug.c
usbstack/iap/libiap/fid-token-values.c
usbstack/iap/libiap/hid.c
usbstack/iap/libiap/iap.c
usbstack/iap/libiap/notification.c
usbstack/iap/libiap/span.c
usbstack/iap/notification.c
usbstack/iap/platform.c
usbstack/usb_iap.c
#endif
#if CONFIG_USBOTG == USBOTG_M66591
drivers/m66591.c
#elif CONFIG_USBOTG == USBOTG_ARC

View file

@ -65,3 +65,7 @@ lcd-as-memframe.c
#endif /* CPU_ARM */
#endif /* LCD_DEPTH */
#endif
#if defined(USE_STACK_PROTECTOR)
stack-protector.c
#endif

View file

@ -19,9 +19,6 @@
*
****************************************************************************/
#define MIXER_OPTIMIZED_WRITE_SAMPLES
#define MIXER_OPTIMIZED_MIX_SAMPLES
/* Mix channels' samples and apply gain factors */
static FORCE_INLINE void mix_samples(void *out,
const void *src0,

View file

@ -19,9 +19,6 @@
*
****************************************************************************/
#define MIXER_OPTIMIZED_WRITE_SAMPLES
#define MIXER_OPTIMIZED_MIX_SAMPLES
/* Mix channels' samples and apply gain factors */
static FORCE_INLINE void mix_samples(void *out,
const void *src0,

View file

@ -18,8 +18,6 @@
* KIND, either express or implied.
*
****************************************************************************/
#define MIXER_OPTIMIZED_MIX_SAMPLES
#define MIXER_OPTIMIZED_WRITE_SAMPLES
/* Mix channels' samples and apply gain factors */
static FORCE_INLINE void mix_samples(void *out,

View file

@ -19,8 +19,6 @@
*
****************************************************************************/
#define MIXER_OPTIMIZED_MIX_SAMPLES
#define MIXER_OPTIMIZED_WRITE_SAMPLES
static struct emac_context
{
unsigned long saved;

View file

@ -0,0 +1,29 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2026 by Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied
*
****************************************************************************/
#include "panic.h"
#include <stdint.h>
const uint32_t __stack_chk_guard = 0x1BADC0DE;
void __stack_chk_fail(void)
{
panicf("stack smashing detected");
}

View file

@ -24,6 +24,7 @@
#if !defined(BOOTLOADER)
#include "settings.h"
#include "action.h"
#include "../apps/gui/skin_engine/skin_engine.h"
#endif
#include <stdlib.h>
#include "cpu.h"
@ -887,6 +888,9 @@ void backlight_set_timeout_plugged(int value)
/* Hold button change event handler. */
void backlight_hold_changed(bool hold_button)
{
#ifndef BOOTLOADER
skin_request_update_locked(hold_button);
#endif
if (!hold_button || (backlight_on_button_hold > 0))
{
/* if unlocked or override in effect */

View file

@ -44,6 +44,10 @@
#define O_NOISODECODE 0
#endif
#ifdef UTF8PROC_EXPORTS
#include "utf8proc.h"
#endif
#define getle16(p) (p[0] | (p[1] << 8))
#define getbe16(p) ((p[0] << 8) | p[1])
@ -59,8 +63,9 @@
#define open_noiso_internal open
#endif /* !APPLICATION */
#if 0 /* not needed just now (will probably end up a spinlock) */
#include "mutex.h"
#if 0 /* not needed just now (will probably end up a spinlock) */
static struct mutex cp_mutex SHAREDBSS_ATTR;
#define cp_lock_init() mutex_init(&cp_mutex)
#define cp_lock_enter() mutex_lock(&cp_mutex)
@ -651,9 +656,32 @@ const char * get_codepage_name(int cp)
return cp_info[cp].name;
}
#if 0 /* not needed just now */
#ifdef UTF8PROC_EXPORTS
static utf8proc_int32_t normbuf[2048];
static struct mutex norm_mutex SHAREDBSS_ATTR;
void utf8_normalize(char *string)
{
utf8proc_ssize_t result, orig;
if (!string || !*string)
return;
mutex_lock(&norm_mutex);
orig = strlen(string);
result = utf8proc_decompose(string, 0, normbuf, sizeof(normbuf)/4 -1, UTF8PROC_NULLTERM);
if (result > 0) {
result = utf8proc_reencode(normbuf, result, UTF8PROC_NULLTERM|UTF8PROC_COMPOSE|UTF8PROC_STABLE);
if (result > 0 && result <= orig && strcmp((char*)normbuf, string))
strcpy(string, (char*)normbuf);
}
mutex_unlock(&norm_mutex);
}
void unicode_init(void)
{
cp_lock_init();
mutex_init(&norm_mutex);
}
#endif

View file

@ -626,18 +626,17 @@ void usb_drv_cancel_all_transfers(void)
endpoints[i].halt[0] = endpoints[i].halt[1] = 1;
}
int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size)
void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep)
{
(void)max_packet_size; /* FIXME: support max packet size override */
(void)type;
(void)endpoint;
return 0;
/* FIXME: support max packet size override */
(void)ctx;
(void)ep;
}
int usb_drv_deinit_endpoint(int endpoint)
void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep)
{
(void)endpoint;
return 0;
(void)ctx;
(void)ep;
}
static void bus_reset(void)

View file

@ -640,8 +640,11 @@ void usb_drv_set_test_mode(int mode) {
M66591_TESTMODE |= mode;
}
int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) {
(void)max_packet_size; /* FIXME: support max packet size override */
void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep) {
/* FIXME: support max packet size override */
const int epnum = EP_NUM(ep);
const int epdir = EP_DIR(ep);
const int type = ctx->type[epnum][epdir];
int pipecfg = 0;
@ -651,41 +654,38 @@ int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size) {
} else if(type == USB_ENDPOINT_XFER_BULK) {
pipecfg |= 1<<13;
} else {
/* Not a supported type */
return -1;
panicf("mxx: unsupported type %d", type);
}
int num = endpoint & USB_ENDPOINT_NUMBER_MASK;
int dir = endpoint & USB_ENDPOINT_DIR_MASK;
if (dir == USB_DIR_IN) {
if (epdir == DIR_IN) {
pipecfg |= (1<<4);
}
M66591_eps[num].dir = dir;
M66591_eps[epnum].dir = epdir == DIR_IN ? USB_DIR_IN : USB_DIR_OUT;
M66591_PIPE_CFGSEL=num;
M66591_PIPE_CFGSEL = epnum;
/* Enable pipe (15) */
pipecfg |= 1<<15;
pipe_handshake(num, PIPE_SHAKE_NAK);
pipe_handshake(epnum, PIPE_SHAKE_NAK);
/* Setup the flags */
M66591_PIPE_CFGWND=pipecfg;
pipe_init(num);
pipe_init(epnum);
logf("mxx: ep req ep#: %d config: 0x%04x", num, M66591_PIPE_CFGWND);
return 0;
logf("mxx: ep req ep#: %d config: 0x%04x", epnum, M66591_PIPE_CFGWND);
}
/* Used by stack to tell the helper functions that the pipe is not in use */
int usb_drv_deinit_endpoint(int endpoint) {
int num = endpoint & USB_ENDPOINT_NUMBER_MASK;
void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep) {
(void)ctx;
int num = ep & USB_ENDPOINT_NUMBER_MASK;
if (num < 1 || num > USB_NUM_ENDPOINTS) {
return -1;
return;
}
int flags = disable_irq_save();
@ -695,8 +695,6 @@ int usb_drv_deinit_endpoint(int endpoint) {
M66591_eps[num].dir = -1;
restore_irq(flags);
return 0;
}
/* Periodically called to check if a cable was plugged into the device */

View file

@ -24,6 +24,8 @@
#include <inttypes.h>
#include <string.h>
#include "usb-designware.h"
#include "config.h"
#include "cpu.h"
#include "system.h"
@ -35,8 +37,6 @@
#include "usb_ch9.h"
#include "usb_core.h"
#include "usb-designware.h"
/* Define LOGF_ENABLE to enable logf output in this file */
/*#define LOGF_ENABLE*/
#include "logf.h"
@ -155,9 +155,6 @@ struct usb_dw_ep0
struct usb_ctrlrequest pending_req;
};
struct usb_drv_ep_spec usb_drv_ep_specs[USB_NUM_ENDPOINTS]; /* filled in usb_drv_init */
uint8_t usb_drv_ep_specs_flags = 0;
static const char* const dw_dir_str[USB_DW_NUM_DIRS] =
{
[USB_DW_EPDIR_IN] = "IN",
@ -195,7 +192,6 @@ static uint32_t usb_endpoints; /* available EPs mask */
(usually 1), otherwise it is the number of dedicated Tx FIFOs
(not counting NPTX FIFO that is always dedicated for IN0). */
static int n_ptxfifos;
static uint16_t ptxfifo_usage;
static uint32_t hw_maxbytes;
static uint32_t hw_maxpackets;
@ -672,47 +668,28 @@ static void usb_dw_unconfigure_ep(int epnum, enum usb_dw_epdir epdir)
#endif
ep_periodic_msk &= ~(1 << epnum);
#endif
ptxfifo_usage &= ~(1 << GET_DTXFNUM(epnum));
}
usb_dw_flush_endpoint(epnum, epdir);
DWC_EPCTL(epnum, epdir) = epctl;
}
static int usb_dw_configure_ep(int epnum,
enum usb_dw_epdir epdir, int type, int maxpktsize)
static void usb_dw_configure_ep(const struct usb_drv_ep_alloc_ctx* ctx, int epnum, enum usb_dw_epdir epdir, int type, int maxpktsize)
{
uint32_t epctl = SETD0PIDEF|EPTYP(type)|USBAEP|maxpktsize;
if (epdir == USB_DW_EPDIR_IN)
if (epdir == USB_DW_EPDIR_IN && ctx->assigned_txfifos[epnum] > 0)
{
/*
* If the hardware has dedicated fifos, we must give each
* IN EP a unique tx-fifo even if it is non-periodic.
*/
#ifdef USB_DW_SHARED_FIFO
ep_periodic_msk |= (1 << epnum);
#ifndef USB_DW_ARCH_SLAVE
epctl |= DWC_DIEPCTL(epnum) & NEXTEP(0xf);
#endif
if (type == USB_ENDPOINT_XFER_INT)
#endif
{
int fnum;
for (fnum = 1; fnum <= n_ptxfifos; fnum++)
if (~ptxfifo_usage & (1 << fnum))
break;
if (fnum > n_ptxfifos)
return -1; /* no available fifos */
ptxfifo_usage |= (1 << fnum);
epctl |= DTXFNUM(fnum);
#ifdef USB_DW_SHARED_FIFO
ep_periodic_msk |= (1 << epnum);
#endif
}
epctl |= DTXFNUM(ctx->assigned_txfifos[epnum]);
}
DWC_EPCTL(epnum, epdir) = epctl;
return 0; /* ok */
}
static void usb_dw_reset_endpoints(void)
@ -753,7 +730,6 @@ static void usb_dw_reset_endpoints(void)
usb_dw_unconfigure_ep(ep, USB_DW_EPDIR_IN);
}
ptxfifo_usage = 0;
#ifdef USB_DW_SHARED_FIFO
ep_periodic_msk = 0;
#endif
@ -1509,17 +1485,6 @@ static void usb_dw_init(void)
/* Soft reconnect */
udelay(3000);
DWC_DCTL &= ~SDIS;
/* Fill endpoint spec table FIXME: should be done in usb_drv_startup() */
usb_drv_ep_specs[0].type[DIR_OUT] = USB_ENDPOINT_XFER_CONTROL;
usb_drv_ep_specs[0].type[DIR_IN] = USB_ENDPOINT_XFER_CONTROL;
for(int i = 1; i < USB_NUM_ENDPOINTS; i += 1) {
bool out_avail = usb_endpoints & (1 << (i + USB_DW_DIR_OFF(USB_DW_EPDIR_OUT)));
usb_drv_ep_specs[i].type[DIR_OUT] = out_avail ? USB_ENDPOINT_TYPE_ANY : USB_ENDPOINT_TYPE_NONE;
bool in_avail = usb_endpoints & (1 << (i + USB_DW_DIR_OFF(USB_DW_EPDIR_IN)));
usb_drv_ep_specs[i].type[DIR_IN] = in_avail ? USB_ENDPOINT_TYPE_ANY : USB_ENDPOINT_TYPE_NONE;
}
}
static void usb_dw_exit(void)
@ -1617,50 +1582,95 @@ void INT_USB_FUNC(void)
usb_dw_irq();
}
int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size)
void usb_drv_ep_reset_alloc_ctx(struct usb_drv_ep_alloc_ctx* ctx)
{
(void)max_packet_size; /* FIXME: support max packet size override */
enum usb_dw_epdir epdir = (EP_DIR(endpoint) == DIR_IN) ? USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT;
struct usb_dw_ep* dw_ep = usb_dw_get_ep(EP_NUM(endpoint), epdir);
int maxpktsize;
if(type == EPTYP_ISOCHRONOUS)
{
maxpktsize = 1023;
}
else
{
maxpktsize = usb_drv_port_speed() ? 512 : 64;
}
usb_dw_target_disable_irq();
int res = usb_dw_configure_ep(EP_NUM(endpoint), epdir, type, maxpktsize);
usb_dw_target_enable_irq();
if(res >= 0)
{
dw_ep->active = true;
return 0;
}
else
{
return -1;
}
memset(ctx, 0, sizeof(*ctx));
}
int usb_drv_deinit_endpoint(int endpoint)
bool usb_drv_ep_allocate(struct usb_drv_ep_alloc_ctx* ctx, int ep, int type, int max_packet_size)
{
enum usb_dw_epdir epdir = (EP_DIR(endpoint) == DIR_IN) ? USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT;
struct usb_dw_ep* dw_ep = usb_dw_get_ep(EP_NUM(endpoint), epdir);
const uint8_t epnum = EP_NUM(ep);
const uint8_t epdir = EP_DIR(ep);
if(ep == EP_CONTROL)
{
return false;
}
enum usb_dw_epdir dwdir = epdir == DIR_IN ? USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT;
if(!(usb_endpoints & (1 << (epnum + USB_DW_DIR_OFF(dwdir)))))
{
return false;
}
bool need_fifo = epdir == DIR_IN;
#ifdef USB_DW_SHARED_FIFO
/* in shared fifo mode, only periodic endpoints need dedicated fifo */
need_fifo &= type == USB_ENDPOINT_XFER_ISOC || type == USB_ENDPOINT_XFER_INT;
#endif
if(!need_fifo)
{
goto ok;
}
for (int fnum = 1; fnum <= n_ptxfifos; fnum++)
{
if (~ctx->txfifo_usage & (1 << fnum))
{
ctx->txfifo_usage |= 1 << fnum;
ctx->assigned_txfifos[epnum] = fnum;
goto ok;
}
}
return false;
ok:
ctx->type[epnum][epdir] = type;
ctx->max_packet_size[epnum][epdir] = max_packet_size;
return true;
}
void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep)
{
const int epnum = EP_NUM(ep);
const int epdir_ = EP_DIR(ep);
const int type = ctx->type[epnum][epdir_];
enum usb_dw_epdir epdir = (epdir_ == DIR_IN) ? USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT;
struct usb_dw_ep* dw_ep = usb_dw_get_ep(EP_NUM(ep), epdir);
int mps = ctx->max_packet_size[epnum][epdir_];
if(mps == -1)
{
if(type == EPTYP_ISOCHRONOUS)
{
mps = 1023;
}
else
{
mps = usb_drv_port_speed() ? 512 : 64;
}
}
usb_dw_target_disable_irq();
usb_dw_unconfigure_ep(EP_NUM(endpoint), epdir);
usb_dw_configure_ep(ctx, epnum, epdir, type, mps);
usb_dw_target_enable_irq();
dw_ep->active = true;
}
void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep)
{
(void)ctx;
enum usb_dw_epdir epdir = (EP_DIR(ep) == DIR_IN) ? USB_DW_EPDIR_IN : USB_DW_EPDIR_OUT;
struct usb_dw_ep* dw_ep = usb_dw_get_ep(EP_NUM(ep), epdir);
usb_dw_target_disable_irq();
usb_dw_unconfigure_ep(EP_NUM(ep), epdir);
usb_dw_target_enable_irq();
dw_ep->active = false;
return 0;
}
int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length)

View file

@ -25,6 +25,7 @@
#include "config.h"
#include <stdbool.h>
#include <stdint.h>
#include <limits.h>
/* ADC channels */
#define AXP2101_ADC_VBAT_VOLTAGE 0

View file

@ -25,6 +25,7 @@
#include "config.h"
#include <stdbool.h>
#include <stdint.h>
#include <limits.h>
/* ADC channels */
#define ADC_ACIN_VOLTAGE 0

View file

@ -1396,6 +1396,14 @@ Lyre prototype 1 */
#define USB_ENABLE_AUDIO
#endif
#if defined(USB_HAS_INTERRUPT) && defined(USB_HAS_ISOCHRONOUS) && USB_VENDOR_ID == 0x05ac
#define USB_ENABLE_IAP
#endif
#if defined(USB_ENABLE_IAP)
#define HAVE_MULTIMEDIA_KEYS
#endif
#endif /* BOOTLOADER */
#endif /* HAVE_USBSTACK */

39
firmware/export/iap-usb.h Normal file
View file

@ -0,0 +1,39 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "config.h"
#ifdef USB_ENABLE_IAP
/* usbstack/iap/notification.c */
void iap_on_track_time_position(uint32_t pos_ms);
void iap_on_track_playback_index(uint32_t index, bool track_ready);
void iap_on_tracks_count(uint32_t count);
void iap_on_play_status(int status /* AUDIO_STATUS_* */);
void iap_on_volume(int volume);
void iap_on_shuffle_state(bool state);
void iap_on_repeat_state(int state);
#else
static inline void iap_on_track_time_position(uint32_t pos_ms) {
(void)pos_ms;
}
static inline void iap_on_track_playback_index(uint32_t index, bool track_ready) {
(void)index;
(void)track_ready;
}
static inline void iap_on_tracks_count(uint32_t count) {
(void)count;
}
static inline void iap_on_play_status(int status) {
(void)status;
}
static inline void iap_on_volume(int volume) {
(void)volume;
}
static inline void iap_on_shuffle_state(bool state) {
(void)state;
}
static inline void iap_on_repeat_state(int state) {
(void)state;
}
#endif

View file

@ -64,6 +64,7 @@ bool pcm_is_initialized(void);
enum pcm_sink_ids pcm_current_sink(void);
const struct pcm_sink_caps* pcm_sink_caps(enum pcm_sink_ids sink);
bool pcm_switch_sink(enum pcm_sink_ids sink);
/* shortcut for plugins */
const struct pcm_sink_caps* pcm_current_sink_caps(void);

View file

@ -23,6 +23,8 @@
#define PCM_MIXER_H
#include <sys/types.h>
#include "pcm.h"
#include "pcm_sink.h"
/** Simple config **/
@ -111,6 +113,9 @@ void mixer_channel_play_pause(enum pcm_mixer_channel channel, bool play);
/* Stop playback on a channel */
void mixer_channel_stop(enum pcm_mixer_channel channel);
/* Switch playback sink */
bool mixer_switch_sink(enum pcm_sink_ids sink);
/* Set channel's amplitude factor */
void mixer_channel_set_amplitude(enum pcm_mixer_channel channel,
unsigned int amplitude);

View file

@ -21,6 +21,8 @@
#include <stddef.h>
#include <stdint.h>
#include "config.h"
struct pcm_sink_caps {
const unsigned long* samprs;
uint16_t num_samprs;
@ -52,6 +54,10 @@ struct pcm_sink {
enum pcm_sink_ids {
PCM_SINK_BUILTIN = 0,
#ifdef USB_ENABLE_IAP
PCM_SINK_IAP,
#endif
PCM_SINK_NUM
};
/* defined in each platform pcm source */

View file

@ -22,7 +22,9 @@
#ifndef __REGGEN_H__
#define __REGGEN_H__
#ifndef __ASSEMBLER__
#include <stdint.h>
#endif
#define __REGGEN_VAR_OR1(p, s1) \
((p ## s1))

View file

@ -26,9 +26,10 @@
#include <stdint.h>
#endif
#define REG16_PTR_T volatile uint16_t *
#define REG32_PTR_T volatile uint32_t *
#define VOID_PTR_PTR_T void* volatile*
#define REG_BIT(x) (1 << (x))
#define REG16_PTR_T volatile uint16_t *
#define REG32_PTR_T volatile uint32_t *
#define VOID_PTR_PTR_T void* volatile*
#if CONFIG_CPU==S5L8700 || CONFIG_CPU==S5L8701
#define CACHEALIGN_BITS (4) /* 2^4 = 16 bytes */
@ -1612,19 +1613,30 @@ Information for them was gathered solely by reverse-engineering Apple's firmware
#define SHA1_BASE 0x38000000
#endif
#define SHA1CONFIG (*((REG32_PTR_T)(SHA1_BASE)))
#define SHA1RESET (*((REG32_PTR_T)(SHA1_BASE + 0x04)))
#define SHA1_CONFIG (*((REG32_PTR_T)(SHA1_BASE)))
#define SHA1_SWRESET (*((REG32_PTR_T)(SHA1_BASE + 0x04)))
#define SHA1_INT_SRC (*((REG32_PTR_T)(SHA1_BASE + 0x08)))
#define SHA1_INT_MASK (*((REG32_PTR_T)(SHA1_BASE + 0x0C)))
#define SHA1_ENDIAN (*((REG32_PTR_T)(SHA1_BASE + 0x10)))
#if CONFIG_CPU == S5L8720
#define SHA1UNK10 (*((REG32_PTR_T)(SHA1_BASE + 0x10)))
#endif
// Result is 20 bytes (160 bits) 0x20-0x33
#define SHA1_RESULT ((REG32_PTR_T)(SHA1_BASE + 0x20))
#define SHA1RESULT ((REG32_PTR_T)(SHA1_BASE + 0x20))
#define SHA1DATAIN ((REG32_PTR_T)(SHA1_BASE + 0x40))
// Input is 64 bytes (512 bits) 0x40-0x7F
#define SHA1_DATA ((REG32_PTR_T)(SHA1_BASE + 0x40))
#if CONFIG_CPU == S5L8720
#define SHA1UNK80 (*((REG32_PTR_T)(SHA1_BASE + 0x80)))
#endif
#define SHA1_MASTER_MODE (*((REG32_PTR_T)(SHA1_BASE + 0x80)))
#define SHA1_MS_START_ADDR (*((REG32_PTR_T)(SHA1_BASE + 0x84)))
#define SHA1_VERSION (*((REG32_PTR_T)(SHA1_BASE + 0x88)))
#define SHA1_MS_SIZE (*((REG32_PTR_T)(SHA1_BASE + 0x8C)))
#define SHA1_FIFO_PARAM (*((REG32_PTR_T)(SHA1_BASE + 0x90)))
#define SHA1_FIFO_CMD (*((REG32_PTR_T)(SHA1_BASE + 0x94)))
#define SHA1_TX_FIFO_STAT (*((REG32_PTR_T)(SHA1_BASE + 0x98)))
#define SHA1_TX_FIFO (*((REG32_PTR_T)(SHA1_BASE + 0xA0)))
#define SHA1_CONFIG_BUSY REG_BIT(0)
#define SHA1_CONFIG_GO REG_BIT(1)
#define SHA1_CONFIG_CONT REG_BIT(3)
/* Clickwheel controller - S5L8701+ */
#if CONFIG_CPU==S5L8701 || CONFIG_CPU==S5L8702 || CONFIG_CPU==S5L8720

View file

@ -266,8 +266,10 @@ static inline void cpu_boost_unlock(void)
#endif
#endif
/* ARM ABIs generally require 8-byte stack alignment */
#ifdef CPU_ARM
/*
* ARM and MIPS ABIs generally require 8-byte stack alignment.
*/
#if defined(CPU_ARM) || defined(CPU_MIPS)
#define MIN_STACK_ALIGN 8
#endif

View file

@ -24,7 +24,9 @@
#define __USB_DESIGNWARE_H__
#include <inttypes.h>
#include <stdbool.h>
#include "config.h"
#include "cpu.h"
#ifndef REG32_PTR_T
#define REG32_PTR_T volatile uint32_t *
@ -288,4 +290,15 @@ extern void usb_dw_target_enable_irq(void);
extern void usb_dw_target_disable_irq(void);
extern void usb_dw_target_clear_irq(void);
/* endpoint allocation */
struct usb_drv_ep_alloc_ctx_dw
{
int8_t type[USB_NUM_ENDPOINTS][2];
int max_packet_size[USB_NUM_ENDPOINTS][2];
uint16_t txfifo_usage;
uint8_t assigned_txfifos[USB_NUM_ENDPOINTS];
};
#define usb_drv_ep_alloc_ctx usb_drv_ep_alloc_ctx_dw
#endif /* __USB_DESIGNWARE_H__ */

View file

@ -172,6 +172,9 @@ enum {
#endif
#ifdef USB_ENABLE_AUDIO
USB_DRIVER_AUDIO,
#endif
#ifdef USB_ENABLE_IAP
USB_DRIVER_IAP,
#endif
USB_NUM_DRIVERS
};

View file

@ -62,19 +62,42 @@ enum usb_control_response {
USB_CONTROL_RECEIVE,
};
/* endpoint allocation:
* there are two ways to implement endpoint allocation.
* 1. define usb_drv_ep_specs and usb_drv_ep_specs_flags in the driver.
* this is the simplest option when the types accepted by each endpoint are mutually independent.
* these variables are set only once during driver initialization and should not be modified afterward.
* 2. define usb_drv_ep_alloc_ctx and implement usb_drv_ep_reset_alloc_ctx and usb_drv_ep_allocate in the driver.
* if the available endpoint types change based on allocation status,
* these functions can be overridden to allow the driver to track the endpoint state.
* */
#ifndef usb_drv_ep_alloc_ctx
/* option 1 */
#define USB_ENDPOINT_TYPE_ANY (-1)
#define USB_ENDPOINT_TYPE_NONE (-2)
struct usb_drv_ep_spec {
int8_t type[2]; /* USB_ENDPOINT_TYPE_{ANY,NONE} USB_ENDPOINT_XFER_* */
};
extern struct usb_drv_ep_spec usb_drv_ep_specs[USB_NUM_ENDPOINTS];
#define USB_ENDPOINT_SPEC_FORCE_IO_TYPE_MATCH (1 << 0)
#define USB_ENDPOINT_SPEC_IO_EXCLUSIVE (1 << 1)
extern uint8_t usb_drv_ep_specs_flags;
struct usb_drv_ep_alloc_ctx {
int8_t type[USB_NUM_ENDPOINTS][2];
int max_packet_size[USB_NUM_ENDPOINTS][2];
};
#else
/* option 2 */
void usb_drv_ep_reset_alloc_ctx(struct usb_drv_ep_alloc_ctx* ctx);
bool usb_drv_ep_allocate(struct usb_drv_ep_alloc_ctx* ctx, int ep, int type, int max_packet_size);
#endif
void usb_drv_ep_init(const struct usb_drv_ep_alloc_ctx* ctx, int ep);
void usb_drv_ep_deinit(const struct usb_drv_ep_alloc_ctx* ctx, int ep);
/* one-time initialisation of the USB driver */
void usb_drv_startup(void);
void usb_drv_int_enable(bool enable); /* Target implemented */
@ -98,8 +121,6 @@ int usb_drv_port_speed(void);
void usb_drv_cancel_all_transfers(void);
void usb_drv_set_test_mode(int mode);
bool usb_drv_connected(void);
int usb_drv_init_endpoint(int endpoint, int type, int max_packet_size);
int usb_drv_deinit_endpoint(int endpoint);
#ifdef USB_HAS_ISOCHRONOUS
/* returns the last received frame number (the 11-bit number contained in the last SOF):
* - full-speed: the host sends one SOF every 1ms (so 1000 SOF/s)

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