mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-11-03 04:22:43 +08:00
[fix] preserve CLI display aspect ratio across terminals
This commit is contained in:
@@ -21,6 +21,22 @@ struct Viewport {
|
||||
bool native = false, pixel_mode = false;
|
||||
int row_offset = 0; // Terminal rows reserved above the video area.
|
||||
};
|
||||
// Keep reported cell geometry across queries; never combine partial replies
|
||||
// from different query cycles. Pixel/cell window sizes use the full text area.
|
||||
class TerminalMetrics {
|
||||
public:
|
||||
void BeginQuery();
|
||||
void Report(const std::string &sequence);
|
||||
void Apply(Viewport &) const;
|
||||
bool Known() const { return cell_width_ > 0; }
|
||||
|
||||
private:
|
||||
bool SetCellSize(double width, double height);
|
||||
double cell_width_ = 0, cell_height_ = 0;
|
||||
int pixel_width_ = 0, pixel_height_ = 0, columns_ = 0, rows_ = 0;
|
||||
bool queried_ = false, direct_reply_ = false;
|
||||
};
|
||||
inline constexpr char kTerminalSizeQuery[] = "\033[16t\033[14t\033[18t";
|
||||
struct Rect {
|
||||
int x, y, width, height;
|
||||
};
|
||||
@@ -72,6 +88,7 @@ public:
|
||||
uint64_t mouse_reports = 0;
|
||||
uint64_t mouse_legacy_reports = 0, mouse_forwarded = 0;
|
||||
uint64_t mouse_send_failures = 0;
|
||||
TerminalMetrics terminal_metrics;
|
||||
|
||||
private:
|
||||
std::string pending_;
|
||||
|
||||
@@ -238,6 +238,10 @@ void Input::Feed(const std::string &bytes, ControllerSession &s,
|
||||
return;
|
||||
std::string seq = pending_.substr(0, end + 1);
|
||||
pending_.erase(0, end + 1);
|
||||
if (seq.back() == 't') {
|
||||
terminal_metrics.Report(seq);
|
||||
continue;
|
||||
}
|
||||
if (seq == "\033[O") {
|
||||
control = false;
|
||||
s.SetControl(false);
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
#include "terminal_renderer.h"
|
||||
#include <charconv>
|
||||
#include <cmath>
|
||||
|
||||
namespace crossdesk::terminal {
|
||||
void TerminalMetrics::BeginQuery() {
|
||||
queried_ = true;
|
||||
direct_reply_ = false;
|
||||
pixel_width_ = pixel_height_ = columns_ = rows_ = 0;
|
||||
}
|
||||
|
||||
bool TerminalMetrics::SetCellSize(double width, double height) {
|
||||
if (width < 1 || height < 1 || width > 1024 || height > 1024 ||
|
||||
width / height < .2 || width / height > 2)
|
||||
return false;
|
||||
cell_width_ = width;
|
||||
cell_height_ = height;
|
||||
return true;
|
||||
}
|
||||
|
||||
void TerminalMetrics::Report(const std::string &sequence) {
|
||||
if (!queried_ || sequence.size() < 9 || sequence.compare(0, 2, "\033[") ||
|
||||
sequence.back() != 't')
|
||||
return;
|
||||
int type = 0, height = 0, width = 0;
|
||||
const char *cur = sequence.data() + 2;
|
||||
const char *end = sequence.data() + sequence.size() - 1;
|
||||
for (int *value : {&type, &height, &width}) {
|
||||
auto result = std::from_chars(cur, end, *value);
|
||||
if (result.ec != std::errc{} || *value <= 0 || *value > 1048576)
|
||||
return;
|
||||
cur = result.ptr;
|
||||
if (value != &width) {
|
||||
if (cur == end || *cur++ != ';')
|
||||
return;
|
||||
} else if (cur != end) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (type == 6) {
|
||||
// CSI 16 t replies with character height, then width (not width/height).
|
||||
if (SetCellSize(width, height))
|
||||
direct_reply_ = true;
|
||||
} else if (type == 4) {
|
||||
pixel_width_ = width;
|
||||
pixel_height_ = height;
|
||||
} else if (type == 8) {
|
||||
columns_ = width;
|
||||
rows_ = height;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
// VTE supports text-area pixels/cells even when CSI 16 t is unavailable.
|
||||
// Use reported full rows/columns, not the capped video viewport or status
|
||||
// rows, and retain fractional cell dimensions for scaled terminal fonts.
|
||||
if (!direct_reply_ && pixel_width_ && pixel_height_ && columns_ && rows_)
|
||||
SetCellSize(double(pixel_width_) / columns_, double(pixel_height_) / rows_);
|
||||
}
|
||||
|
||||
void TerminalMetrics::Apply(Viewport &viewport) const {
|
||||
if (!Known())
|
||||
return;
|
||||
viewport.cell_aspect = cell_width_ / cell_height_;
|
||||
viewport.pixel_width = int(std::lround(cell_width_ * viewport.columns));
|
||||
viewport.pixel_height = int(std::lround(cell_height_ * viewport.rows));
|
||||
}
|
||||
} // namespace crossdesk::terminal
|
||||
Reference in New Issue
Block a user