BTRFS: Implement BTree::Path and change _Find.
[haiku.git] / src / apps / serialconnect / TermView.cpp
bloba57f8fcb8db7c5f9777c5aece4e64417280887da
1 /*
2 * Copyright 2012-2015, Adrien Destugues, pulkomandy@gmail.com
3 * Distributed under the terms of the MIT licence.
4 */
7 #include "TermView.h"
9 #include <stdio.h>
11 #include <Entry.h>
12 #include <File.h>
13 #include <Font.h>
14 #include <Layout.h>
15 #include <ScrollBar.h>
17 #include "SerialApp.h"
18 #include "libvterm/src/vterm_internal.h"
21 struct ScrollBufferItem {
22 int cols;
23 VTermScreenCell cells[];
27 TermView::TermView()
29 BView("TermView", B_WILL_DRAW | B_FRAME_EVENTS)
31 _Init();
35 TermView::TermView(BRect r)
37 BView(r, "TermView", B_FOLLOW_ALL_SIDES, B_WILL_DRAW | B_FRAME_EVENTS)
39 _Init();
43 TermView::~TermView()
45 vterm_free(fTerm);
49 void
50 TermView::AttachedToWindow()
52 BView::AttachedToWindow();
53 MakeFocus();
57 void
58 TermView::Draw(BRect updateRect)
60 VTermRect updatedChars = _PixelsToGlyphs(updateRect);
62 VTermPos pos;
63 font_height height;
64 GetFontHeight(&height);
66 VTermPos cursorPos;
67 vterm_state_get_cursorpos(vterm_obtain_state(fTerm), &cursorPos);
69 for (pos.row = updatedChars.start_row; pos.row <= updatedChars.end_row;
70 pos.row++) {
71 int x = updatedChars.start_col * fFontWidth + kBorderSpacing;
72 int y = pos.row * fFontHeight + (int)ceil(height.ascent)
73 + kBorderSpacing;
74 MovePenTo(x, y);
76 BString string;
77 VTermScreenCell cell;
78 int width = 0;
79 bool isCursor = false;
81 pos.col = updatedChars.start_col;
82 _GetCell(pos, cell);
84 for (pos.col = updatedChars.start_col;
85 pos.col <= updatedChars.end_col;) {
87 VTermScreenCell newCell;
88 _GetCell(pos, newCell);
90 // We need to start a new extent if:
91 // - The attributes change
92 // - The colors change
93 // - The end of line is reached
94 // - The current cell is under the cursor
95 // - The current cell is right of the cursor
96 if (*(uint32_t*)&cell.attrs != *(uint32_t*)&newCell.attrs
97 || !vterm_color_equal(cell.fg, newCell.fg)
98 || !vterm_color_equal(cell.bg, newCell.bg)
99 || pos.col >= updatedChars.end_col
100 || (pos.col == cursorPos.col && pos.row == cursorPos.row)
101 || (pos.col == cursorPos.col + 1 && pos.row == cursorPos.row)) {
103 rgb_color foreground, background;
104 foreground.red = cell.fg.red;
105 foreground.green = cell.fg.green;
106 foreground.blue = cell.fg.blue;
107 foreground.alpha = 255;
108 background.red = cell.bg.red;
109 background.green = cell.bg.green;
110 background.blue = cell.bg.blue;
111 background.alpha = 255;
113 // Draw the cursor by swapping foreground and background colors
114 if (isCursor) {
115 SetLowColor(foreground);
116 SetViewColor(foreground);
117 SetHighColor(background);
118 } else {
119 SetLowColor(background);
120 SetViewColor(background);
121 SetHighColor(foreground);
124 FillRect(BRect(x, y - ceil(height.ascent) + 1,
125 x + width * fFontWidth - 1,
126 y + ceil(height.descent) + ceil(height.leading)),
127 B_SOLID_LOW);
129 BFont font = be_fixed_font;
130 if (cell.attrs.bold)
131 font.SetFace(B_BOLD_FACE);
132 if (cell.attrs.underline)
133 font.SetFace(B_UNDERSCORE_FACE);
134 if (cell.attrs.italic)
135 font.SetFace(B_ITALIC_FACE);
136 if (cell.attrs.blink) // FIXME make it actually blink
137 font.SetFace(B_OUTLINED_FACE);
138 if (cell.attrs.reverse)
139 font.SetFace(B_NEGATIVE_FACE);
140 if (cell.attrs.strike)
141 font.SetFace(B_STRIKEOUT_FACE);
143 // TODO handle "font" (alternate fonts), dwl and dhl (double size)
145 SetFont(&font);
146 DrawString(string);
147 x += width * fFontWidth;
149 // Prepare for next cell
150 cell = newCell;
151 string = "";
152 width = 0;
155 if (pos.col == cursorPos.col && pos.row == cursorPos.row)
156 isCursor = true;
157 else
158 isCursor = false;
160 if (newCell.chars[0] == 0) {
161 string += " ";
162 pos.col ++;
163 width += 1;
164 } else {
165 char buffer[VTERM_MAX_CHARS_PER_CELL];
166 wcstombs(buffer, (wchar_t*)newCell.chars,
167 VTERM_MAX_CHARS_PER_CELL);
168 string += buffer;
169 width += newCell.width;
170 pos.col += newCell.width;
177 void
178 TermView::FrameResized(float width, float height)
180 VTermRect newSize = _PixelsToGlyphs(BRect(0, 0, width - 2 * kBorderSpacing,
181 height - 2 * kBorderSpacing));
182 vterm_set_size(fTerm, newSize.end_row, newSize.end_col);
186 void
187 TermView::GetPreferredSize(float* width, float* height)
189 if (width != NULL)
190 *width = kDefaultWidth * fFontWidth + 2 * kBorderSpacing - 1;
191 if (height != NULL)
192 *height = kDefaultHeight * fFontHeight + 2 * kBorderSpacing - 1;
196 void
197 TermView::KeyDown(const char* bytes, int32 numBytes)
199 // Translate some keys to more usual VT100 escape codes
200 switch (bytes[0]) {
201 case B_UP_ARROW:
202 numBytes = 3;
203 bytes = "\x1B[A";
204 break;
205 case B_DOWN_ARROW:
206 numBytes = 3;
207 bytes = "\x1B[B";
208 break;
209 case B_RIGHT_ARROW:
210 numBytes = 3;
211 bytes = "\x1B[C";
212 break;
213 case B_LEFT_ARROW:
214 numBytes = 3;
215 bytes = "\x1B[D";
216 break;
217 case B_BACKSPACE:
218 numBytes = 1;
219 bytes = "\x7F";
220 break;
221 case '\n':
222 numBytes = fLineTerminator.Length();
223 bytes = fLineTerminator.String();
224 break;
227 // Send the bytes to the serial port
228 BMessage* keyEvent = new BMessage(kMsgDataWrite);
229 keyEvent->AddData("data", B_RAW_TYPE, bytes, numBytes);
230 be_app_messenger.SendMessage(keyEvent);
234 void
235 TermView::MessageReceived(BMessage* message)
237 switch(message->what)
239 case 'DATA':
241 entry_ref ref;
242 if (message->FindRef("refs", &ref) == B_OK)
244 // The user just dropped a file on us
245 // TODO send it by XMODEM or so
247 break;
249 default:
250 BView::MessageReceived(message);
255 void
256 TermView::SetLineTerminator(BString terminator)
258 fLineTerminator = terminator;
262 void
263 TermView::PushBytes(const char* bytes, size_t length)
265 vterm_push_bytes(fTerm, bytes, length);
269 // #pragma mark -
272 void
273 TermView::_Init()
275 SetFont(be_fixed_font);
277 font_height height;
278 GetFontHeight(&height);
279 fFontHeight = (int)(ceilf(height.ascent) + ceilf(height.descent)
280 + ceilf(height.leading));
281 fFontWidth = (int)be_fixed_font->StringWidth("X");
282 fTerm = vterm_new(kDefaultHeight, kDefaultWidth);
284 fTermScreen = vterm_obtain_screen(fTerm);
285 vterm_screen_set_callbacks(fTermScreen, &sScreenCallbacks, this);
286 vterm_screen_reset(fTermScreen, 1);
288 vterm_parser_set_utf8(fTerm, 1);
290 VTermScreenCell cell;
291 VTermPos firstPos;
292 firstPos.row = 0;
293 firstPos.col = 0;
294 _GetCell(firstPos, cell);
296 rgb_color background;
297 background.red = cell.bg.red;
298 background.green = cell.bg.green;
299 background.blue = cell.bg.blue;
300 background.alpha = 255;
302 SetViewColor(background);
303 SetLineTerminator("\n");
307 VTermRect
308 TermView::_PixelsToGlyphs(BRect pixels) const
310 pixels.OffsetBy(-kBorderSpacing, -kBorderSpacing);
312 VTermRect rect;
313 rect.start_col = (int)floor(pixels.left / fFontWidth);
314 rect.end_col = (int)ceil(pixels.right / fFontWidth);
315 rect.start_row = (int)floor(pixels.top / fFontHeight);
316 rect.end_row = (int)ceil(pixels.bottom / fFontHeight);
317 #if 0
318 printf(
319 "TOP %d ch < %f px\n"
320 "BTM %d ch < %f px\n"
321 "LFT %d ch < %f px\n"
322 "RGH %d ch < %f px\n",
323 rect.start_row, pixels.top,
324 rect.end_row, pixels.bottom,
325 rect.start_col, pixels.left,
326 rect.end_col, pixels.right
328 #endif
329 return rect;
333 BRect TermView::_GlyphsToPixels(const VTermRect& glyphs) const
335 BRect rect;
336 rect.top = glyphs.start_row * fFontHeight;
337 rect.bottom = glyphs.end_row * fFontHeight;
338 rect.left = glyphs.start_col * fFontWidth;
339 rect.right = glyphs.end_col * fFontWidth;
341 rect.OffsetBy(kBorderSpacing, kBorderSpacing);
342 #if 0
343 printf(
344 "TOP %d ch > %f px (%f)\n"
345 "BTM %d ch > %f px\n"
346 "LFT %d ch > %f px (%f)\n"
347 "RGH %d ch > %f px\n",
348 glyphs.start_row, rect.top, fFontHeight,
349 glyphs.end_row, rect.bottom,
350 glyphs.start_col, rect.left, fFontWidth,
351 glyphs.end_col, rect.right
353 #endif
354 return rect;
358 BRect
359 TermView::_GlyphsToPixels(const int width, const int height) const
361 VTermRect rect;
362 rect.start_row = 0;
363 rect.start_col = 0;
364 rect.end_row = height;
365 rect.end_col = width;
366 return _GlyphsToPixels(rect);
370 void
371 TermView::_GetCell(VTermPos pos, VTermScreenCell& cell)
373 // First handle cells from the normal screen
374 if (vterm_screen_get_cell(fTermScreen, pos, &cell) != 0)
375 return;
377 // Try the scroll-back buffer
378 if (pos.row < 0 && pos.col >= 0) {
379 int offset = - pos.row - 1;
380 ScrollBufferItem* line
381 = (ScrollBufferItem*)fScrollBuffer.ItemAt(offset);
382 if (line != NULL && pos.col < line->cols) {
383 cell = line->cells[pos.col];
384 return;
388 // All cells outside the used terminal area are drawn with the same
389 // background color as the top-left one.
390 // TODO should they use the attributes of the closest neighbor instead?
391 VTermPos firstPos;
392 firstPos.row = 0;
393 firstPos.col = 0;
394 vterm_screen_get_cell(fTermScreen, firstPos, &cell);
395 cell.chars[0] = 0;
396 cell.width = 1;
400 void
401 TermView::_Damage(VTermRect rect)
403 Invalidate(_GlyphsToPixels(rect));
407 void
408 TermView::_MoveCursor(VTermPos pos, VTermPos oldPos, int visible)
410 VTermRect r;
412 // We need to erase the cursor from its old position
413 r.start_row = oldPos.row;
414 r.start_col = oldPos.col;
415 r.end_col = oldPos.col + 1;
416 r.end_row = oldPos.row + 1;
417 Invalidate(_GlyphsToPixels(r));
419 // And we need to draw it at the new one
420 r.start_row = pos.row;
421 r.start_col = pos.col;
422 r.end_col = pos.col + 1;
423 r.end_row = pos.row + 1;
424 Invalidate(_GlyphsToPixels(r));
428 void
429 TermView::_PushLine(int cols, const VTermScreenCell* cells)
431 ScrollBufferItem* item = (ScrollBufferItem*)malloc(sizeof(int)
432 + cols * sizeof(VTermScreenCell));
433 item->cols = cols;
434 memcpy(item->cells, cells, cols * sizeof(VTermScreenCell));
436 fScrollBuffer.AddItem(item, 0);
438 free(fScrollBuffer.RemoveItem(kScrollBackSize));
440 int availableRows, availableCols;
441 vterm_get_size(fTerm, &availableRows, &availableCols);
443 VTermRect dirty;
444 dirty.start_col = 0;
445 dirty.end_col = availableCols;
446 dirty.end_row = 0;
447 dirty.start_row = -fScrollBuffer.CountItems();
448 // FIXME we should rather use CopyRect if possible, and only invalidate the
449 // newly exposed area here.
450 Invalidate(_GlyphsToPixels(dirty));
452 BScrollBar* scrollBar = ScrollBar(B_VERTICAL);
453 if (scrollBar != NULL) {
454 float range = (fScrollBuffer.CountItems() + availableRows)
455 * fFontHeight;
456 scrollBar->SetRange(availableRows * fFontHeight - range, 0.0f);
457 // TODO we need to adjust this in FrameResized, as availableRows can
458 // change
459 scrollBar->SetProportion(availableRows * fFontHeight / range);
460 scrollBar->SetSteps(fFontHeight, fFontHeight * 3);
465 /* static */ int
466 TermView::_Damage(VTermRect rect, void* user)
468 TermView* view = (TermView*)user;
469 view->_Damage(rect);
471 return 0;
475 /* static */ int
476 TermView::_MoveCursor(VTermPos pos, VTermPos oldPos, int visible, void* user)
478 TermView* view = (TermView*)user;
479 view->_MoveCursor(pos, oldPos, visible);
481 return 0;
485 /* static */ int
486 TermView::_PushLine(int cols, const VTermScreenCell* cells, void* user)
488 TermView* view = (TermView*)user;
489 view->_PushLine(cols, cells);
491 return 0;
495 const
496 VTermScreenCallbacks TermView::sScreenCallbacks = {
497 &TermView::_Damage,
498 /*.moverect =*/ NULL,
499 &TermView::_MoveCursor,
500 /*.settermprop =*/ NULL,
501 /*.setmousefunc =*/ NULL,
502 /*.bell =*/ NULL,
503 /*.resize =*/ NULL,
504 &TermView::_PushLine,
505 /*.sb_popline =*/ NULL,