Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
- The OPDS browser shows how many books a category holds, in parentheses next to its arrow, when the catalog provides a count (a `thr:count` link attribute or a "12713 books" summary). Folder titles that start with a 📁 emoji show as "/name" instead, since the device fonts have no folder emoji.
- On devices with PSRAM (Sticky, X4 Pro), the OPDS browser downloads the next page of a catalog in the background while you browse, and keeps pages you have visited in memory, so Next page, Previous page and Back open without waiting on the server.
- The web file manager's image preview has previous and next buttons, and the left and right arrow keys, to step through the images in the current folder. Other file types are skipped, and stepping wraps around at either end. The preview also shows the image's position in the folder, its pixel dimensions, its file size and, when the file list has one, its modified date.
- The Settings > System footer shows the firmware's branch (the batch number, such as `b11`, for combined test builds) and commit, with `*` when built from uncommitted changes, under the version. The System list stops above the footer instead of running under it. The web status API reports the full branch, build number (the commit count unless the build sets `CROSSINK_BUILD_NUMBER`; left out for shallow checkouts) and UTC build time.

### Changed

Expand All @@ -32,6 +33,7 @@

### Fixed

- X4 Pro light-sleep firmware shows its real version (for example `1.6.0-x4-pro`) instead of "dev" in Settings, on the boot screen, and in the web and OTA version checks.
- Background Library indexing and the reader's next-page draw-ahead run at full CPU speed again instead of the lowest idle clock.
- USB Drive no longer reads ahead into the sectors a computer is about to write, so copying files to the card is not slowed by background reads.
- Nearby and KOReader position sync read the chapter layout of the orientation the book is read in, instead of whichever orientation the sync screen happened to use, so a book read in landscape lands on the right page.
Expand Down
63 changes: 60 additions & 3 deletions scripts/git_branch.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"""

import configparser
import datetime
import os
import re
import subprocess
Expand Down Expand Up @@ -100,6 +101,54 @@ def get_git_dirty(project_dir):
return 'unknown'


def get_build_number(project_dir):
# Batch/CI builds may pass their own number; otherwise use the commit count,
# which only means something when the full history is checked out. Shallow
# CI checkouts would all report 1, so they get no number at all.
number = os.environ.get('CROSSINK_BUILD_NUMBER')
if number:
return sanitize_version_component(number)
if run_git_value(project_dir, ['rev-parse', '--is-shallow-repository'], 'shallow state') != 'false':
return ''
number = run_git_value(project_dir, ['rev-list', '--count', 'HEAD'], 'build number')
return number if number.isdigit() else ''


def short_branch_label(branch):
# Batch branches (test/combined-0927-b11) show as their batch number; other
# branches drop their prefix folder (claude/, feature/, fix/).
batch = re.search(r'combined-\d+-(b\d+)$', branch)
if batch:
return batch.group(1)
return branch.rsplit('/', 1)[-1] or 'unknown'


def register_build_info(env, project_dir):
# The build time changes on every build. Defining it globally would change
# every compile command and force a full rebuild, so scope these defines to
# the one small file that exposes them.
branch = run_git_value(
project_dir, ['rev-parse', '--abbrev-ref', 'HEAD'], 'branch'
)
if branch == 'HEAD':
branch = 'detached'
branch = re.sub(r'[^A-Za-z0-9._/-]+', '-', branch) or 'unknown'
build_time = datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%dT%H:%MZ')
defines = [
('CROSSINK_GIT_BRANCH', f'\\"{branch}\\"'),
('CROSSINK_GIT_BRANCH_SHORT', f'\\"{short_branch_label(branch)}\\"'),
('CROSSINK_BUILD_NUMBER', f'\\"{get_build_number(project_dir)}\\"'),
('CROSSINK_BUILD_TIME', f'\\"{build_time}\\"'),
]

def add_build_info_defines(node_env, node):
build_env = node_env.Clone()
build_env.Append(CPPDEFINES=defines)
return build_env.Object(node)

env.AddBuildMiddleware(add_build_info_defines, '*src/util/BuildInfo.cpp')


def _read_ini(project_dir):
ini_path = os.path.join(project_dir, 'platformio.ini')
local_ini_path = os.path.join(project_dir, 'platformio.local.ini')
Expand Down Expand Up @@ -156,7 +205,12 @@ def get_hardware_version(project_dir, pioenv):
if os.environ.get('CROSSINK_RELEASE_VERSION')
else get_crossink_version(project_dir)
)
device_suffix = {'sticky': '-sticky', 'x4-pro': '-x4-pro', 'x4-classic': '-x4-classic'}[pioenv]
device_suffix = {
'sticky': '-sticky',
'x4-pro': '-x4-pro',
'x4-pro-light-sleep': '-x4-pro',
'x4-classic': '-x4-classic',
}[pioenv]
return f'{base_version}{device_suffix}'


Expand All @@ -172,7 +226,10 @@ def inject_version(env):
('CROSSINK_PIOENV', f'\\"{pioenv}\\"'),
])

if pioenv in {'default', 'sticky', 'x4-pro', 'x4-classic'}:
if hasattr(env, 'AddBuildMiddleware'):
register_build_info(env, project_dir)

if pioenv in {'default', 'sticky', 'x4-pro', 'x4-pro-light-sleep', 'x4-classic'}:
version_string = get_hardware_version(project_dir, pioenv)
if os.environ.get('CROSSINK_RC_HASH'):
print(f'CrossInk RC build version: {version_string}')
Expand Down Expand Up @@ -206,7 +263,7 @@ def inject_version(env):
])
print(f'CrossInk test build version: {ci_version}{suffix}')

elif pioenv in {'x4-pro-debug', 'x4-classic-debug'}:
elif pioenv in {'x4-pro-debug', 'x4-pro-light-sleep-debug', 'x4-classic-debug'}:
branch = get_git_branch(project_dir)
short_hash = get_git_short_hash(project_dir)
ci_version = get_crossink_version(project_dir)
Expand Down
32 changes: 27 additions & 5 deletions src/activities/settings/SettingsActivity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
#include "components/UiAppHelpers.h"
#include "components/icons/frontlightHeaderIcons.h"
#include "fontIds.h"
#include "util/BuildInfo.h"
#include "util/DictionaryRegistry.h"
#include "util/FrontlightSchedule.h"

Expand Down Expand Up @@ -148,12 +149,31 @@ std::string formatCompactDuration(const uint32_t seconds) {
return buf;
}

constexpr const char* systemVersionLabel = "CrossInk " CROSSINK_VERSION;

// Space below the settings list for the System footer: the build details line
// plus the version on one line, or two when it has to wrap.
int systemVersionFooterReserve(const GfxRenderer& renderer, const int pageWidth, const ThemeMetrics& metrics) {
const int maxWidth = pageWidth - systemVersionFooterSideMargin * 2;
const int versionLines = renderer.getTextWidth(SMALL_FONT_ID, systemVersionLabel) <= maxWidth ? 1 : 2;
return metrics.verticalSpacing + systemVersionFooterBottomInset +
versionLines * renderer.getLineHeight(SMALL_FONT_ID);
}

void drawSystemVersionFooter(const GfxRenderer& renderer, const int pageWidth, const int pageHeight,
const ThemeMetrics& metrics) {
const std::string label = "CrossInk " CROSSINK_VERSION;
const std::string label = systemVersionLabel;
const int maxWidth = pageWidth - systemVersionFooterSideMargin * 2;
const int bottomLineY =
const int lineHeight = renderer.getLineHeight(SMALL_FONT_ID);
const int detailsLineY =
pageHeight - metrics.buttonHintsHeight - metrics.verticalSpacing - systemVersionFooterBottomInset;
const int bottomLineY = detailsLineY - lineHeight;

// Short branch and commit; "*" marks uncommitted changes.
char details[64];
snprintf(details, sizeof(details), "%s %s%s", BuildInfo::shortBranch(), CROSSINK_GIT_SHA,
strcmp(CROSSINK_GIT_DIRTY, "1") == 0 ? "*" : "");
drawCenteredTextLine(renderer, pageWidth, detailsLineY, renderer.truncatedText(SMALL_FONT_ID, details, maxWidth));

if (renderer.getTextWidth(SMALL_FONT_ID, label.c_str()) <= maxWidth) {
drawCenteredTextLine(renderer, pageWidth, bottomLineY, label);
Expand Down Expand Up @@ -182,7 +202,6 @@ void drawSystemVersionFooter(const GfxRenderer& renderer, const int pageWidth, c
const std::string secondLine = lineBreak == std::string::npos
? ""
: renderer.truncatedText(SMALL_FONT_ID, label.substr(lineBreak).c_str(), maxWidth);
const int lineHeight = renderer.getLineHeight(SMALL_FONT_ID);
drawCenteredTextLine(renderer, pageWidth, bottomLineY - lineHeight, firstLine);
drawCenteredTextLine(renderer, pageWidth, bottomLineY, secondLine);
}
Expand Down Expand Up @@ -1374,8 +1393,11 @@ void SettingsActivity::buildSettingsScreen(UiApp::ScreenType& screen) {
// compact header geometry is in absolute screen coordinates. Overlap the
// tab's top rule with the header's final underline pixel.
const int tabTop = std::max<int>(safe.y, CompactHeader::headerBottomY(metrics) - 1);
screen.setContentMargin(
fui::Insets{static_cast<int16_t>(tabTop - safe.y), 0, static_cast<int16_t>(metrics.buttonHintsHeight), 0});
int bottomMargin = metrics.buttonHintsHeight;
if (!isFileBrowserView() && selectedCategoryIndex == 3) {
bottomMargin += systemVersionFooterReserve(renderer, renderer.getScreenWidth(), metrics);
}
screen.setContentMargin(fui::Insets{static_cast<int16_t>(tabTop - safe.y), 0, static_cast<int16_t>(bottomMargin), 0});

if (isFileBrowserView()) {
screen.spacer(static_cast<int16_t>(metrics.verticalSpacing));
Expand Down
4 changes: 4 additions & 0 deletions src/network/CrossPointWebServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
#include "html/js/jszip_minJs.generated.h"
#include "util/BookCacheUtils.h"
#include "util/BootReason.h"
#include "util/BuildInfo.h"
#include "util/FontFamilyLabel.h"
#include "util/StringUtils.h"

Expand Down Expand Up @@ -776,6 +777,9 @@ void CrossPointWebServer::handleStatus() const {
build["env"] = CROSSINK_PIOENV;
build["gitSha"] = CROSSINK_GIT_SHA;
build["gitDirty"] = CROSSINK_GIT_DIRTY;
build["gitBranch"] = BuildInfo::gitBranch();
build["buildNumber"] = BuildInfo::buildNumber();
build["buildTime"] = BuildInfo::buildTime();

#ifndef SIMULATOR
const BoardConfig::BoardProfile& board = BoardConfig::ACTIVE;
Expand Down
24 changes: 24 additions & 0 deletions src/util/BuildInfo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#include "BuildInfo.h"

#ifndef CROSSINK_GIT_BRANCH
#define CROSSINK_GIT_BRANCH "unknown"
#endif

#ifndef CROSSINK_GIT_BRANCH_SHORT
#define CROSSINK_GIT_BRANCH_SHORT CROSSINK_GIT_BRANCH
#endif

#ifndef CROSSINK_BUILD_NUMBER
#define CROSSINK_BUILD_NUMBER ""
#endif

#ifndef CROSSINK_BUILD_TIME
#define CROSSINK_BUILD_TIME "unknown"
#endif

namespace BuildInfo {
const char* gitBranch() { return CROSSINK_GIT_BRANCH; }
const char* shortBranch() { return CROSSINK_GIT_BRANCH_SHORT; }
const char* buildNumber() { return CROSSINK_BUILD_NUMBER; }
const char* buildTime() { return CROSSINK_BUILD_TIME; }
} // namespace BuildInfo
13 changes: 13 additions & 0 deletions src/util/BuildInfo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#pragma once

// Per-build provenance for the Settings > System footer. Kept out of
// AppVersion.h because scripts/git_branch.py defines these only for
// BuildInfo.cpp: the build time changes every build, and a global define would
// force a full rebuild each time.
namespace BuildInfo {
const char* gitBranch();
// Batch number ("b11") for batch branches, else the branch without its prefix folder.
const char* shortBranch();
const char* buildNumber();
const char* buildTime();
} // namespace BuildInfo
Loading