1
0

Implemented nickel_orientation action (closes #67) (#70)

This commit is contained in:
Patrick Gaskin
2020-08-05 19:31:39 -04:00
committed by GitHub
parent 6ee67452a2
commit 300f11ddad
3 changed files with 306 additions and 120 deletions

220
res/doc
View File

@@ -21,89 +21,100 @@
# library - the menu in the filter bar for the "My Books" and "My Articles" library views
# <label> the label to show on the menu item (must not contain :)
# <action> the type of action to run, one of:
# cmd_spawn - starts a command in the background
# cmd_output - runs a command, waits for it to exit, and optionally displays the output
# dbg_syslog - writes a message to syslog (for testing)
# dbg_error - always returns an error (for testing)
# dbg_msg - shows a message (for testing)
# dbg_toast - shows a toast (for testing)
# kfmon - triggers a kfmon action
# nickel_setting - changes a setting
# nickel_extras - opens one of the beta features
# nickel_browser - opens the browser
# nickel_misc - other stuff which isn't significant enough for its own category
# nickel_open - opens a view
# nickel_wifi - controls wifi (note: it doesn't wait for it to connect or disconnect, neither does it check for success)
# power - gracefully controls the power state
# skip - skips a number of actions after the current one (mainly useful for more complex conditional chains) (this action will not update the success/failure flag)
# cmd_spawn - starts a command in the background
# cmd_output - runs a command, waits for it to exit, and optionally displays the output
# dbg_syslog - writes a message to syslog (for testing)
# dbg_error - always returns an error (for testing)
# dbg_msg - shows a message (for testing)
# dbg_toast - shows a toast (for testing)
# kfmon - triggers a kfmon action
# nickel_setting - changes a setting
# nickel_extras - opens one of the beta features
# nickel_browser - opens the browser
# nickel_misc - other stuff which isn't significant enough for its own category
# nickel_open - opens a view
# nickel_wifi - controls wifi (note: it doesn't wait for it to connect or disconnect, neither does it check for success)
# nickel_orientation - controls screen orientation
# (devices without an orientation sensor need to use the kobopatch patch "Allow rotation on all devices" or set [DeveloperSettings] ForceAllowLandscape=true)
# (devices with an orientation sensor don't need to do anything, but can set the config setting to make this work on all views)
# (this will override the rotation icon/popup until it is set to something different or the device is rebooted)
# power - gracefully controls the power state
# skip - skips a number of actions after the current one (mainly useful for more complex conditional chains) (this action will not update the success/failure flag)
# <arg> the argument passed to the action:
# cmd_spawn - the command line to pass to /bin/sh -c (started in /)
# It can be prefixed with "quiet:" to prevent the toast with the process PID from being displayed.
# cmd_output - the timeout in milliseconds (0 < t < 10000), a colon, then the command line to pass to /bin/sh -c (started in /)
# It can be prefixed with "quiet:" to prevent the message box with the output from being displayed (i.e. you'd use this where you'd normally use >/dev/null 2>&1).
# dbg_syslog - the text to write
# dbg_error - the error message
# dbg_msg - the message
# dbg_toast - the message
# kfmon - the filename of the KFMon watched item to launch.
# This is actually the basename of the watch's filename as specified in its KFMon config (i.e., the png).
# You can also check the output of the 'list' command via the kfmon-ipc tool.
# nickel_setting - <action>:<setting>
# action is one of:
# toggle - toggles between true/false
# enable - sets to true
# disable - sets to false
# setting is one of:
# invert - FeatureSettings.InvertScreen
# lockscreen - PowerSettings.UnlockEnabled (4.12.12111+)
# screenshots - FeatureSettings.Screenshots
# force_wifi - DeveloperSettings.ForceWifiOn (note: the setting doesn't apply until you toggle WiFi)
# nickel_extras - the mimetype of the plugin, or one of:
# unblock_it
# sketch_pad
# solitaire
# sudoku
# word_scramble
# nickel_browser - one of:
# - opens the web browser to the default homepage (note that the line should end with a colon even though the argument is blank)
# <url> - opens the web browser to the specified URL
# <url> <css> - opens the web browser to the specified URL and injects the specified CSS (which can contain spaces and colons) into all pages
# modal - opens the web browser to the default homepage as a pop-up window
# modal:<url> - see above
# modal:<url> <css> - see above
# nickel_misc - one of:
# home - goes to the home screen
# force_usb_connection - forces a usb connection dialog to be shown
# rescan_books - forces nickel to rescan books (4.13.12638+)
# rescan_books_full - forces a full usb connect/disconnect cycle (4.13.12638+)
# nickel_open - one of:
# discover:storefront - Kobo Store
# discover:wishlist - Wishlist
# library:library - My Books (with last filter)
# library:library2 - My Books (with last tab and filter)
# library:all - Books
# library:authors - Authors
# library:series - Series (4.20.14601+)
# library:shelves - Collections
# library:pocket - Articles
# library:dropbox - Dropbox (4.18.13737+)
# reading_life:reading_life - Activity (with last tab)
# reading_life:stats - Activity
# reading_life:awards - Awards
# reading_life:words - My Words
# store:overdrive - OverDrive (4.10.11655+) (note: if you don't have an active OverDrive account, it will give you a "Network Error")
# store:search - Search
# nickel_wifi - one of:
# autoconnect - attempts to enable and connect to wifi (similar to what happens when you open a link)
# autoconnect_silent - attempts to connect to wifi in the background (does nothing if wifi is disabled, the battery is low, is already connected, or there aren't any known networks in range) (no errors are shown) (similar to what happens when you turn on the Kobo)
# enable - enables WiFi (but doesn't necessarily connect to it)
# disable - disables WiFi
# toggle - toggles WiFi (but doesn't necessarily connect to it)
# power - one of:
# shutdown (4.13.12638+)
# reboot (4.13.12638+)
# sleep (4.13.12638+)
# skip - the number of actions to skip, or -1 to skip all remaining ones (i.e. end the chain)
# cmd_spawn - the command line to pass to /bin/sh -c (started in /)
# It can be prefixed with "quiet:" to prevent the toast with the process PID from being displayed.
# cmd_output - the timeout in milliseconds (0 < t < 10000), a colon, then the command line to pass to /bin/sh -c (started in /)
# It can be prefixed with "quiet:" to prevent the message box with the output from being displayed (i.e. you'd use this where you'd normally use >/dev/null 2>&1).
# dbg_syslog - the text to write
# dbg_error - the error message
# dbg_msg - the message
# dbg_toast - the message
# kfmon - the filename of the KFMon watched item to launch.
# This is actually the basename of the watch's filename as specified in its KFMon config (i.e., the png).
# You can also check the output of the 'list' command via the kfmon-ipc tool.
# nickel_setting - <action>:<setting>
# action is one of:
# toggle - toggles between true/false
# enable - sets to true
# disable - sets to false
# setting is one of:
# invert - FeatureSettings.InvertScreen
# lockscreen - PowerSettings.UnlockEnabled (4.12.12111+)
# screenshots - FeatureSettings.Screenshots
# force_wifi - DeveloperSettings.ForceWifiOn (note: the setting doesn't apply until you toggle WiFi)
# nickel_extras - the mimetype of the plugin, or one of:
# unblock_it
# sketch_pad
# solitaire
# sudoku
# word_scramble
# nickel_browser - one of:
# - opens the web browser to the default homepage (note that the line should end with a colon even though the argument is blank)
# <url> - opens the web browser to the specified URL
# <url> <css> - opens the web browser to the specified URL and injects the specified CSS (which can contain spaces and colons) into all pages
# modal - opens the web browser to the default homepage as a pop-up window
# modal:<url> - see above
# modal:<url> <css> - see above
# nickel_misc - one of:
# home - goes to the home screen
# force_usb_connection - forces a usb connection dialog to be shown
# rescan_books - forces nickel to rescan books (4.13.12638+)
# rescan_books_full - forces a full usb connect/disconnect cycle (4.13.12638+)
# nickel_open - one of:
# discover:storefront - Kobo Store
# discover:wishlist - Wishlist
# library:library - My Books (with last filter)
# library:library2 - My Books (with last tab and filter)
# library:all - Books
# library:authors - Authors
# library:series - Series (4.20.14601+)
# library:shelves - Collections
# library:pocket - Articles
# library:dropbox - Dropbox (4.18.13737+)
# reading_life:reading_life - Activity (with last tab)
# reading_life:stats - Activity
# reading_life:awards - Awards
# reading_life:words - My Words
# store:overdrive - OverDrive (4.10.11655+) (note: if you don't have an active OverDrive account, it will give you a "Network Error")
# store:search - Search
# nickel_wifi - one of:
# autoconnect - attempts to enable and connect to wifi (similar to what happens when you open a link)
# autoconnect_silent - attempts to connect to wifi in the background (does nothing if wifi is disabled, the battery is low, is already connected, or there aren't any known networks in range) (no errors are shown) (similar to what happens when you turn on the Kobo)
# enable - enables WiFi (but doesn't necessarily connect to it)
# disable - disables WiFi
# toggle - toggles WiFi (but doesn't necessarily connect to it)
# nickel_orientation - one of:
# portrait (4.13.12638+)
# landscape (4.13.12638+)
# inverted_portrait (4.13.12638+)
# inverted_landscape (4.13.12638+)
# invert (4.13.12638+) - Toggles between inverted/non-inverted (preserves side) (this will not work if used in a chain with swap)
# swap (4.13.12638+) - Toggles between portrait/landscape (preserves inversion) (this will not work if used in a chain with invert)
# power - one of:
# shutdown (4.13.12638+)
# reboot (4.13.12638+)
# sleep (4.13.12638+)
# skip - the number of actions to skip, or -1 to skip all remaining ones (i.e. end the chain)
#
# chain_success:<action>:<arg>
# chain_failure:<action>:<arg>
@@ -135,31 +146,32 @@
#
# For example, you might have a configuration file named "mystuff" like:
#
# menu_item :main :Show an Error :dbg_error :This is an error message!
# menu_item :main :Do Nothing :cmd_spawn :sleep 60
# menu_item :main :Dump Syslog :cmd_spawn :logread > /mnt/onboard/.adds/syslog.log
# menu_item :main :Kernel Version :cmd_output :500:uname -a
# menu_item :main :Sketch Pad :nickel_extras :sketch_pad
# menu_item :main :Plato :kfmon :plato.png
# menu_item :main :Show an Error :dbg_error :This is an error message!
# menu_item :main :Do Nothing :cmd_spawn :sleep 60
# menu_item :main :Dump Syslog :cmd_spawn :logread > /mnt/onboard/.adds/syslog.log
# menu_item :main :Kernel Version :cmd_output :500:uname -a
# menu_item :main :Sketch Pad :nickel_extras :sketch_pad
# menu_item :main :Plato :kfmon :plato.png
# generator :main :kfmon
# menu_item :reader :Invert Screen :nickel_setting :toggle :invert
# menu_item :main :IP Address :cmd_output :500:/sbin/ifconfig | /usr/bin/awk '/inet addr/{print substr($2,6)}'
# menu_item :main :Telnet :cmd_spawn :quiet:/bin/mount -t devpts | /bin/grep -q /dev/pts || { /bin/mkdir -p /dev/pts && /bin/mount -t devpts devpts /dev/pts; }
# chain_success :cmd_spawn :quiet:/usr/bin/pkill -f "^/usr/bin/tcpsvd -E 0.0.0.0 1023" || true && exec /usr/bin/tcpsvd -E 0.0.0.0 1023 /usr/sbin/telnetd -i -l /bin/login
# chain_success :dbg_toast :Started Telnet server on port 1023.
# menu_item :main :FTP :cmd_spawn :quiet:/usr/bin/pkill -f "^/usr/bin/tcpsvd -E 0.0.0.0 1021" || true && exec /usr/bin/tcpsvd -E 0.0.0.0 1021 /usr/sbin/ftpd -w -t 30 /mnt/onboard
# chain_success :dbg_toast :Started FTP server for KOBOeReader partition on port 1021.
# menu_item :main :Telnet (toggle) :cmd_output :500:quiet :/usr/bin/pkill -f "^/usr/bin/tcpsvd -E 0.0.0.0 2023"
# menu_item :reader :Invert Screen :nickel_setting :toggle :invert
# menu_item :reader :Invert Orientation :nickel_orientation :invert
# menu_item :main :IP Address :cmd_output :500:/sbin/ifconfig | /usr/bin/awk '/inet addr/{print substr($2,6)}'
# menu_item :main :Telnet :cmd_spawn :quiet:/bin/mount -t devpts | /bin/grep -q /dev/pts || { /bin/mkdir -p /dev/pts && /bin/mount -t devpts devpts /dev/pts; }
# chain_success :cmd_spawn :quiet:/usr/bin/pkill -f "^/usr/bin/tcpsvd -E 0.0.0.0 1023" || true && exec /usr/bin/tcpsvd -E 0.0.0.0 1023 /usr/sbin/telnetd -i -l /bin/login
# chain_success :dbg_toast :Started Telnet server on port 1023.
# menu_item :main :FTP :cmd_spawn :quiet:/usr/bin/pkill -f "^/usr/bin/tcpsvd -E 0.0.0.0 1021" || true && exec /usr/bin/tcpsvd -E 0.0.0.0 1021 /usr/sbin/ftpd -w -t 30 /mnt/onboard
# chain_success :dbg_toast :Started FTP server for KOBOeReader partition on port 1021.
# menu_item :main :Telnet (toggle) :cmd_output :500:quiet :/usr/bin/pkill -f "^/usr/bin/tcpsvd -E 0.0.0.0 2023"
# chain_success:skip:5
# chain_failure :cmd_spawn :quiet :/bin/mount -t devpts | /bin/grep -q /dev/pts || { /bin/mkdir -p /dev/pts && /bin/mount -t devpts devpts /dev/pts; }
# chain_success :cmd_spawn :quiet :exec /usr/bin/tcpsvd -E 0.0.0.0 2023 /usr/sbin/telnetd -i -l /bin/login
# chain_success :dbg_toast :Started Telnet server on port 2023
# chain_failure :dbg_toast :Error starting Telnet server on port 2023
# chain_failure :cmd_spawn :quiet :/bin/mount -t devpts | /bin/grep -q /dev/pts || { /bin/mkdir -p /dev/pts && /bin/mount -t devpts devpts /dev/pts; }
# chain_success :cmd_spawn :quiet :exec /usr/bin/tcpsvd -E 0.0.0.0 2023 /usr/sbin/telnetd -i -l /bin/login
# chain_success :dbg_toast :Started Telnet server on port 2023
# chain_failure :dbg_toast :Error starting Telnet server on port 2023
# chain_always:skip:-1
# chain_success :dbg_toast :Stopped Telnet server on port 2023
# menu_item :library :Import books :nickel_misc :rescan_books_full
# menu_item :browser :Invert Screen :nickel_setting :toggle :invert
# menu_item :browser :Open Pop-Up :nickel_browser :modal
# chain_success :dbg_toast :Stopped Telnet server on port 2023
# menu_item :library :Import books :nickel_misc :rescan_books_full
# menu_item :browser :Invert Screen :nickel_setting :toggle :invert
# menu_item :browser :Open Pop-Up :nickel_browser :modal
#
# You will need to reboot to see any changes.
#

View File

@@ -35,22 +35,23 @@ void nm_action_result_free(nm_action_result_t *res);
#define NM_ACTION_(name) nm_action_result_t *NM_ACTION(name)(const char *arg)
#endif
#define NM_ACTIONS \
X(cmd_spawn) \
X(cmd_output) \
X(dbg_syslog) \
X(dbg_error) \
X(dbg_msg) \
X(dbg_toast) \
X(kfmon) \
X(kfmon_id) \
X(nickel_setting) \
X(nickel_extras) \
X(nickel_browser) \
X(nickel_misc) \
X(nickel_open) \
X(nickel_wifi) \
X(power) \
#define NM_ACTIONS \
X(cmd_spawn) \
X(cmd_output) \
X(dbg_syslog) \
X(dbg_error) \
X(dbg_msg) \
X(dbg_toast) \
X(kfmon) \
X(kfmon_id) \
X(nickel_setting) \
X(nickel_extras) \
X(nickel_browser) \
X(nickel_misc) \
X(nickel_open) \
X(nickel_wifi) \
X(nickel_orientation) \
X(power) \
X(skip)
#define X(name) NM_ACTION_(name);

View File

@@ -1,5 +1,6 @@
#include <QApplication>
#include <QProcess>
#include <QScreen>
#include <QString>
#include <QStringList>
#include <QUrl>
@@ -51,6 +52,7 @@ typedef void BrowserWorkflowManager;
typedef void N3SettingsExtrasController;
typedef void N3PowerWorkflowManager;
typedef void WirelessWorkflowManager;
typedef void StatusBarView;
NM_ACTION_(nickel_open) {
char *tmp1 = strdupa(arg); // strsep and strtrim will modify it
@@ -552,6 +554,177 @@ NM_ACTION_(nickel_wifi) {
return nm_action_result_silent();
}
NM_ACTION_(nickel_orientation) {
Qt::ScreenOrientation o;
if (!strcmp(arg, "portrait")) o = Qt::PortraitOrientation;
else if (!strcmp(arg, "landscape")) o = Qt::LandscapeOrientation;
else if (!strcmp(arg, "inverted_portrait")) o = Qt::InvertedPortraitOrientation;
else if (!strcmp(arg, "inverted_landscape")) o = Qt::InvertedLandscapeOrientation;
else if (!strcmp(arg, "invert") || !strcmp(arg, "swap")) {
switch ((o = QGuiApplication::primaryScreen()->orientation())) {
case Qt::PrimaryOrientation: NM_ERR_RET(nullptr, "could not get current screen orientation"); break;
case Qt::PortraitOrientation: o = !strcmp(arg, "invert") ? Qt::InvertedPortraitOrientation : Qt::LandscapeOrientation; break;
case Qt::LandscapeOrientation: o = !strcmp(arg, "invert") ? Qt::InvertedLandscapeOrientation : Qt::PortraitOrientation; break;
case Qt::InvertedPortraitOrientation: o = !strcmp(arg, "invert") ? Qt::PortraitOrientation : Qt::InvertedLandscapeOrientation; break;
case Qt::InvertedLandscapeOrientation: o = !strcmp(arg, "invert") ? Qt::LandscapeOrientation : Qt::InvertedPortraitOrientation; break;
default: NM_ERR_RET(nullptr, "unknown screen orientation %d", o); break;
}
}
else NM_ERR_RET(nullptr, "unknown nickel_orientation action '%s'", arg);
// ---
//libnickel 4.6 * _ZN22QWindowSystemInterface29handleScreenOrientationChangeEP7QScreenN2Qt17ScreenOrientationE
void (*QWindowSystemInterface_handleScreenOrientationChange)(QScreen*, Qt::ScreenOrientation);
reinterpret_cast<void*&>(QWindowSystemInterface_handleScreenOrientationChange) = dlsym(RTLD_DEFAULT, "_ZN22QWindowSystemInterface29handleScreenOrientationChangeEP7QScreenN2Qt17ScreenOrientationE");
NM_CHECK(nullptr, QWindowSystemInterface_handleScreenOrientationChange, "could not dlsym QWindowSystemInterface::handleScreenOrientationChange (did the way Nickel handles the screen orientation sensor change?)");
//libnickel 4.6 * _ZN6Device16getCurrentDeviceEv
Device *(*Device_getCurrentDevice)();
reinterpret_cast<void*&>(Device_getCurrentDevice) = dlsym(RTLD_DEFAULT, "_ZN6Device16getCurrentDeviceEv");
NM_CHECK(nullptr, Device_getCurrentDevice, "could not dlsym Device::getCurrentDevice");
//libnickel 4.11.11911 * _ZNK6Device20hasOrientationSensorEv
bool (*Device_hasOrientationSensor)(Device*);
reinterpret_cast<void*&>(Device_hasOrientationSensor) = dlsym(RTLD_DEFAULT, "_ZNK6Device20hasOrientationSensorEv");
NM_CHECK(nullptr, Device_getCurrentDevice, "could not dlsym Device::hasOrientationSensor");
//libnickel 4.6 * _ZN8SettingsC2ERK6Deviceb _ZN8SettingsC2ERK6Device
void *(*Settings_Settings)(Settings*, Device*, bool);
void *(*Settings_SettingsLegacy)(Settings*, Device*);
reinterpret_cast<void*&>(Settings_Settings) = dlsym(RTLD_DEFAULT, "_ZN8SettingsC2ERK6Deviceb");
reinterpret_cast<void*&>(Settings_SettingsLegacy) = dlsym(RTLD_DEFAULT, "_ZN8SettingsC2ERK6Device");
NM_CHECK(nullptr, Settings_Settings || Settings_SettingsLegacy, "could not dlsym Settings constructor (new and/or old)");
//libnickel 4.6 * _ZN8SettingsD2Ev
void *(*Settings_SettingsD)(Settings*);
reinterpret_cast<void*&>(Settings_SettingsD) = dlsym(RTLD_DEFAULT, "_ZN8SettingsD2Ev");
NM_CHECK(nullptr, Settings_SettingsD, "could not dlsym Settings destructor");
void *ApplicationSettings_vtable = dlsym(RTLD_DEFAULT, "_ZTV19ApplicationSettings");
NM_CHECK(nullptr, ApplicationSettings_vtable, "could not dlsym the vtable for ApplicationSettings");
//libnickel 4.13.12638 * _ZN19ApplicationSettings20setLockedOrientationE6QFlagsIN2Qt17ScreenOrientationEE
bool (*ApplicationSettings_setLockedOrientation)(Settings*, Qt::ScreenOrientation);
reinterpret_cast<void*&>(ApplicationSettings_setLockedOrientation) = dlsym(RTLD_DEFAULT, "_ZN19ApplicationSettings20setLockedOrientationE6QFlagsIN2Qt17ScreenOrientationEE");
NM_CHECK(nullptr, ApplicationSettings_setLockedOrientation, "could not dlsym ApplicationSettings::setLockedOrientation");
//libnickel 4.13.12638 * _ZN19ApplicationSettings17lockedOrientationEv
int (*ApplicationSettings_lockedOrientation)(Settings*);
reinterpret_cast<void*&>(ApplicationSettings_lockedOrientation) = dlsym(RTLD_DEFAULT, "_ZN19ApplicationSettings17lockedOrientationEv");
NM_CHECK(nullptr, ApplicationSettings_lockedOrientation, "could not dlsym ApplicationSettings::lockedOrientation");
Device *dev = Device_getCurrentDevice();
NM_CHECK(nullptr, dev, "could not get shared nickel device pointer");
Settings *settings = alloca(128); // way larger than it is, but better to be safe
if (Settings_Settings)
Settings_Settings(settings, dev, false);
else if (Settings_SettingsLegacy)
Settings_SettingsLegacy(settings, dev);
#define vtable_ptr(x) *reinterpret_cast<void**&>(x)
#define vtable_target(x) reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(x)+8)
// ---
// note: these notes were last updated for 4.22.15268, but should remain
// correct for the forseeable future and should be relevant for all
// supported versions (4.13.12638+).
// Prevent the sensor (if active) from changing to a different orientation
// behind our backs.
//
// Without this, the orientation sensor may override our orientation if the
// sensor is in auto mode, and may invert our orientation if the sensor is
// in portrait/landscape mode. This would happen the moment the sensor
// reading is updated, which would be essentially instantly if the device
// isn't on a perfectly still surface.
QGuiApplication::primaryScreen()->setOrientationUpdateMask(o);
// Set the current locked orientation to the new one to ensure our new
// orientation will be allowed.
//
// Without this, our orientation may not have any effect since the
// orientation set by QWindowSystemInterface::handleScreenOrientationChange
// is limited by if the orientation's bit is set in [ApplicationSettings]
// LockedOrientation (note that auto is all of them, and portrait/landscape
// is the normal and inverted variants) (it's a bitmask of
// Qt::ScreenOrientation).
vtable_ptr(settings) = vtable_target(ApplicationSettings_vtable);
int icon_mask = ApplicationSettings_lockedOrientation(settings);
vtable_ptr(settings) = vtable_target(ApplicationSettings_vtable);
ApplicationSettings_setLockedOrientation(settings, o);
// If the device doesn't have an orientation sensor, the user needs to set
// the [DeveloperSettings] ForceAllowLandscape setting to true or apply the
// "Allow rotation on all devices" patch (the patch will also show the
// built-in rotation menu in the reader and other views). If this is not
// done, this action will silently fail to work. We can't really check this
// specifically since kobopatch only changes a few places which call
// Device::hasOrientationSensor, not the function itself.
//
// If the device has an orientation sensor, the user can optionally set the
// [DeveloperSettings] ForceAllowLandscape setting to true or apply the
// "Allow rotation on all devices" patch to make the rotation take effect
// even if the current view doesn't normally allow it.
//
// This is because in addition to the previous limitation, if
// [DeveloperSettings] ForceAllowLandscape if not true, the orientation is
// limited based on if Device::hasOrientationSensor and the allowed
// orientations for the current view.
NM_LOG(Device_hasOrientationSensor(dev)
? "nickel_orientation: if this didn't do anything, you may need to set [DeveloperSettings] ForceAllowLandscape=true to allow landscape orientations on all views"
: "nickel_orientation: if this didn't do anything, ensure you have applied the 'Allow rotation on all devices' kobopatch patch, or that you have set [DeveloperSettings] ForceAllowLandscape=true");
// Set the orientation.
//
// This is the only thing which is really required here, but if used alone,
// the orientation sensor may override it, and the orientation will only be
// set if it is within the constraints of the current rotation mode of
// auto/portrait/landscape.
//
// Note that this function is actually part of the private Qt QPA API, and
// Nickel really should be doing this part of the rotation logic (including
// the sensors) from the libkobo QPA plugin. But, this is how Nickel does
// it, so we need to do it the same way (this does make it easier for both
// us and Kobo, I guess, due to how intertwined the orientation stuff is).
//
// This is the same function which is called by Nickel's handler for the
// orientation sensor's reading.
QWindowSystemInterface_handleScreenOrientationChange(QGuiApplication::primaryScreen(), o);
// We don't update the status bar rotation icon, since we would need to get
// ahold of the StatusBarView, and I don't like the current options for
// doing that in a version-agnostic way: walking the entire QObject tree,
// hooking the constructor, or using offsets directly to get ahold of the
// StatusBarView from the StatusBarController from the MainWindowController.
// I might reconsider this in the future if there is demand for it.
if ((icon_mask&o) == 0)
NM_LOG("nickel_orientation: the status bar rotate icon may be out of date (this is expected)");
// If we ever wanted to add an option for setting to rotation to auto/locked
// portrait/locked landscape, we'd need to call RotatePopupController
// ::on{Auto,Portrait,Landscape} on StatusBarView::rotatePopupController.
// This is how the rotate popup and the dropdown in the reading settings
// does it.
// ---
#undef vtable_ptr
#undef vtable_target
Settings_SettingsD(settings);
return nm_action_result_silent();
}
NM_ACTION_(cmd_spawn) {
char *tmp = strdup(arg); // strsep and strtrim will modify it
char *tmp1 = tmp; // so we can still free tmp later