-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Can xterm export more bufferline / chardata classes? #2105
Comments
If you want to modify the terminal contents the best way to do that is to use term.buffer.getLine(y).getCell(x).char This API could change, mainly depending on how #2005 pans out. Lines 930 to 1012 in 739723f
|
I second @Tyriar's answer - direct buffer manipulation should not be encouraged by any API, its way to easy to break things with such an approach (thats the main reason why |
I'll close this off as it's possible via write and the upcoming API, I definitely still want us to support #595 which is probably related |
Basically, my approach currently is to have the server emit "placeholder" invisible characters that the client fills in later by modifying its own buffer. I'm not very familiar with the VT100 escape sequences, so any guidance you can provide is very much appreciated. What would be the best way to make the cursor jump to an arbitrary point in the past, write out some new characters, and then return to where it was previously? |
@vincentwoo grab the cursor position from const row = 2;
const col = 3;
term.write(`\x1b[${row};${col}Hfoo`); |
Just to clarify:
|
|
I've taken the time to develop this technique, and at first I thought absolute CUP was working for our needs. Unfortunately, what I've found is that you cannot overwrite data that is scrolled outside of the current viewport with CUP. This was what was sort of nice about reaching into the buffer in our previous approach - xterm would correctly render what we wanted when scrolling. Do you have any other suggestions for how to achieve this effect? |
The plan is to expose the actual |
Hm. That may help for reads, but I assume that also means there's no plan to allow editing the buffer from outside? |
Thats true. Altering buffer content directly has to trigger several post actions, otherwise things can go wrong in very weird ways. Scrollback is seen as static in all emulators, none allows to alter content here. Our input handling with render triggers is bound to cursor actions and cursor adressible area (the lower rows in the terminal buffer), changing that would clearly break with the de-facto terminal standard. Thus we cannot do that. For your problem at hand I see two possible hacky solutions:
In general I dont recommend the second way above, do this only if you have enough resources to put into the maintenance overhead. |
I ended up just writing the internal packed array directly. This is probably not super sustainable, but it is somewhat straightforward to express once you understand the new buffer format. If this is of interest to any other anonymous readers out there: // writing msg to point at row, col in the buffer
const lineData = term.buffer.getLine(row)._line._data
for (let i = 0; i < msg.length; i++) {
lineData[(col + i) * /* CELL_SIZE */ 3 + /* Cell.CONTENT */ 0] =
msg.charCodeAt(i) | (1 << /* Content.WIDTH_SHIFT */ 22)
}
term.refresh(row, row) |
@vincentwoo I'd say you've found the most unreliable way to do it skipping really everything that those methods of bufferline try to encapsulate. Dont do this at home kids! 🤣 Seriously, this is broken in many ways, at least you should set the correct wcwidth and do proper UTF16 --> UTF32 conversion. Also attribs should be set. |
Ya, I just happen to know the source text in this case is safe, all single-width chars. Definitely not a scalaeble solution :) |
@vincentwoo Sorry I cannot let this go uncommented as it will fail at to many conditions and should not be used by others if they dont understand these limitations. The "official" way to set buffer content from a string would look more like this (untested): import { StringToUtf32 } from 'common/input/TextDecoder';
import { wcwidth } from 'common/CharWidth';
const someString = '...';
const buffer = new Uint32Array(someString.length);
const stringDecoder = new StringToUtf32();
const length = stringDecoder.decode(someString, buffer);
// some start row
let bufferRow = term.buffer.getLine(...)._line;
// some start col offset
let pos = ...;
let lastPos = pos;
// some attrib fg / bg attrs (see common/buffer/Constants.ts for memory layout)
const fg = ...;
const bg = ...;
// walk the data
for (let i = 0; i < length; ++i) {
const code = buffer[i];
const chWidth = wcwidth(code);
if (!chWidth) {
// we have a combining / diacritical mark, should add to last active cell
bufferRow.addCodepointToCell(lastPos, code);
continue;
}
if (pos + chWidth >= bufferRow.length) {
// re-position if we write beyond last cell in line
bufferRow = ...;
pos = ...;
}
lastPos = pos;
bufferRow.setCellFromCodePoint(pos++, code, chWidth, fg, bg);
if (chWidth > 0) {
while (--cellWidth) {
// wide char found, thus we have to add empty cells behind
bufferRow.setCellFromCodePoint(pos++, 0, 0, fg, bg);
}
}
}
// since we didnt track the current scroll offset simply trigger
// a whole refresh when done
term._core?._dirtyRowService?.markDirty(0, term.rows); Please dont try to circumvent basic guards (as you do above with writing directly to the memory), its as bad as putting some random data to a |
Since handling attribs above is still a nightmare here a way to make it easier (untested): import { Params } from 'common/parser/Params';
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
// store current terminal attrs
const savedAttr = term._core.curAttrData;
// start with default attrs
term._core.curAttrData = DEFAULT_ATTR_DATA.clone();
// now you can use Inputhandler.charAttributes to do the nasty attr construction for you
// it takes SGR params e.g. CSI 1;31 m for bold red fg --> [1, 31]
term._core._inputHandler.charAttributes(Params.fromArray([1, 31]));
const fg = term._core.curAttrData.fg;
const bg = term._core.curAttrData.bg;
// do the stuff above
...
// never forget to restore old attrs when done
term._core.curAttrData = savedAttr; |
Did you consider serializing with the (wip) serialize addon, amending the buffer and writing that after calling |
ah, love 2 learn how to do things by posting the wrong way to do things :) |
If we want to modify the terminal contents from the outside, we do need to reach into the terminal guts on occasion. It would be nice to be able to easily instantiate CellData, for instance. Could we export more of these at the top level xterm module?
The text was updated successfully, but these errors were encountered: