1 /* 2 * Copyright 2013, Haiku, Inc. All rights reserved. 3 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. 4 * Distributed under the terms of the MIT License. 5 * 6 * Authors: 7 * Ingo Weinhold, ingo_weinhold@gmx.de 8 * Siarzhuk Zharski, zharik@gmx.li 9 */ 10 11 #include "TerminalBuffer.h" 12 13 #include <algorithm> 14 15 #include <Message.h> 16 17 #include "Colors.h" 18 #include "TermApp.h" 19 #include "TermConst.h" 20 21 22 // #pragma mark - public methods 23 24 25 TerminalBuffer::TerminalBuffer() 26 : 27 BLocker("terminal buffer"), 28 fEncoding(M_UTF8), 29 fAlternateScreen(NULL), 30 fAlternateHistory(NULL), 31 fAlternateScreenOffset(0), 32 fAlternateAttributes(0), 33 fColorsPalette(NULL), 34 fListenerValid(false) 35 { 36 } 37 38 39 TerminalBuffer::~TerminalBuffer() 40 { 41 delete fAlternateScreen; 42 delete fAlternateHistory; 43 delete[] fColorsPalette; 44 } 45 46 47 status_t 48 TerminalBuffer::Init(int32 width, int32 height, int32 historySize) 49 { 50 if (Sem() < 0) 51 return Sem(); 52 53 fAlternateScreen = _AllocateLines(width, height); 54 if (fAlternateScreen == NULL) 55 return B_NO_MEMORY; 56 57 for (int32 i = 0; i < height; i++) 58 fAlternateScreen[i]->Clear(); 59 60 fColorsPalette = new(std::nothrow) rgb_color[kTermColorCount]; 61 if (fColorsPalette == NULL) 62 return B_NO_MEMORY; 63 64 for (uint i = 0; i < kTermColorCount; i++) 65 fColorsPalette[i] = TermApp::DefaultPalette()[i]; 66 67 return BasicTerminalBuffer::Init(width, height, historySize); 68 } 69 70 71 void 72 TerminalBuffer::SetListener(BMessenger listener) 73 { 74 fListener = listener; 75 fListenerValid = true; 76 } 77 78 79 void 80 TerminalBuffer::UnsetListener() 81 { 82 fListenerValid = false; 83 } 84 85 86 int 87 TerminalBuffer::Encoding() const 88 { 89 return fEncoding; 90 } 91 92 93 void 94 TerminalBuffer::ReportX10MouseEvent(bool reportX10MouseEvent) 95 { 96 if (fListenerValid) { 97 BMessage message(MSG_REPORT_MOUSE_EVENT); 98 message.AddBool("reportX10MouseEvent", reportX10MouseEvent); 99 fListener.SendMessage(&message); 100 } 101 } 102 103 104 void 105 TerminalBuffer::ReportNormalMouseEvent(bool reportNormalMouseEvent) 106 { 107 if (fListenerValid) { 108 BMessage message(MSG_REPORT_MOUSE_EVENT); 109 message.AddBool("reportNormalMouseEvent", reportNormalMouseEvent); 110 fListener.SendMessage(&message); 111 } 112 } 113 114 115 void 116 TerminalBuffer::ReportButtonMouseEvent(bool report) 117 { 118 if (fListenerValid) { 119 BMessage message(MSG_REPORT_MOUSE_EVENT); 120 message.AddBool("reportButtonMouseEvent", report); 121 fListener.SendMessage(&message); 122 } 123 } 124 125 126 void 127 TerminalBuffer::ReportAnyMouseEvent(bool reportAnyMouseEvent) 128 { 129 if (fListenerValid) { 130 BMessage message(MSG_REPORT_MOUSE_EVENT); 131 message.AddBool("reportAnyMouseEvent", reportAnyMouseEvent); 132 fListener.SendMessage(&message); 133 } 134 } 135 136 137 void 138 TerminalBuffer::SetEncoding(int encoding) 139 { 140 fEncoding = encoding; 141 } 142 143 144 void 145 TerminalBuffer::SetTitle(const char* title) 146 { 147 if (fListenerValid) { 148 BMessage message(MSG_SET_TERMINAL_TITLE); 149 message.AddString("title", title); 150 fListener.SendMessage(&message); 151 } 152 } 153 154 155 void 156 TerminalBuffer::SetColors(uint8* indexes, rgb_color* colors, 157 int32 count, bool dynamic) 158 { 159 if (fListenerValid) { 160 BMessage message(MSG_SET_TERMINAL_COLORS); 161 message.AddInt32("count", count); 162 message.AddBool("dynamic", dynamic); 163 message.AddData("index", B_UINT8_TYPE, 164 indexes, sizeof(uint8), true, count); 165 message.AddData("color", B_RGB_COLOR_TYPE, 166 colors, sizeof(rgb_color), true, count); 167 168 for (int i = 1; i < count; i++) { 169 message.AddData("index", B_UINT8_TYPE, &indexes[i], sizeof(uint8)); 170 message.AddData("color", B_RGB_COLOR_TYPE, &colors[i], 171 sizeof(rgb_color)); 172 } 173 174 fListener.SendMessage(&message); 175 } 176 } 177 178 179 void 180 TerminalBuffer::ResetColors(uint8* indexes, int32 count, bool dynamic) 181 { 182 if (fListenerValid) { 183 BMessage message(MSG_RESET_TERMINAL_COLORS); 184 message.AddInt32("count", count); 185 message.AddBool("dynamic", dynamic); 186 message.AddData("index", B_UINT8_TYPE, 187 indexes, sizeof(uint8), true, count); 188 189 for (int i = 1; i < count; i++) 190 message.AddData("index", B_UINT8_TYPE, &indexes[i], sizeof(uint8)); 191 192 fListener.SendMessage(&message); 193 } 194 } 195 196 197 void 198 TerminalBuffer::SetCursorStyle(int32 style, bool blinking) 199 { 200 if (fListenerValid) { 201 BMessage message(MSG_SET_CURSOR_STYLE); 202 message.AddInt32("style", style); 203 message.AddBool("blinking", blinking); 204 fListener.SendMessage(&message); 205 } 206 } 207 208 209 void 210 TerminalBuffer::SetCursorBlinking(bool blinking) 211 { 212 if (fListenerValid) { 213 BMessage message(MSG_SET_CURSOR_STYLE); 214 message.AddBool("blinking", blinking); 215 fListener.SendMessage(&message); 216 } 217 } 218 219 220 void 221 TerminalBuffer::SetCursorHidden(bool hidden) 222 { 223 if (fListenerValid) { 224 BMessage message(MSG_SET_CURSOR_STYLE); 225 message.AddBool("hidden", hidden); 226 fListener.SendMessage(&message); 227 } 228 } 229 230 231 void 232 TerminalBuffer::SetPaletteColor(uint8 index, rgb_color color) 233 { 234 fColorsPalette[index] = color; 235 } 236 237 238 rgb_color 239 TerminalBuffer::PaletteColor(uint8 index) 240 { 241 return fColorsPalette[index]; 242 } 243 244 245 int 246 TerminalBuffer::GuessPaletteColor(int red, int green, int blue) 247 { 248 int distance = 255 * 100; 249 int index = -1; 250 for (uint32 i = 0; i < kTermColorCount && distance > 0; i++) { 251 rgb_color color = fColorsPalette[i]; 252 int r = 30 * abs(color.red - red); 253 int g = 59 * abs(color.green - green); 254 int b = 11 * abs(color.blue - blue); 255 int d = r + g + b; 256 if (distance > d) { 257 index = i; 258 distance = d; 259 } 260 } 261 262 return min_c(index, int(kTermColorCount - 1)); 263 } 264 265 266 void 267 TerminalBuffer::NotifyQuit(int32 reason) 268 { 269 if (fListenerValid) { 270 BMessage message(MSG_QUIT_TERMNAL); 271 message.AddInt32("reason", reason); 272 fListener.SendMessage(&message); 273 } 274 } 275 276 277 void 278 TerminalBuffer::NotifyListener() 279 { 280 if (fListenerValid) 281 fListener.SendMessage(MSG_TERMINAL_BUFFER_CHANGED); 282 } 283 284 285 status_t 286 TerminalBuffer::ResizeTo(int32 width, int32 height) 287 { 288 int32 historyCapacity = 0; 289 if (!fAlternateScreenActive) 290 historyCapacity = HistoryCapacity(); 291 else if (fAlternateHistory != NULL) 292 historyCapacity = fAlternateHistory->Capacity(); 293 294 return ResizeTo(width, height, historyCapacity); 295 } 296 297 298 status_t 299 TerminalBuffer::ResizeTo(int32 width, int32 height, int32 historyCapacity) 300 { 301 // switch to the normal screen buffer first 302 bool alternateScreenActive = fAlternateScreenActive; 303 if (alternateScreenActive) 304 _SwitchScreenBuffer(); 305 306 int32 oldWidth = fWidth; 307 int32 oldHeight = fHeight; 308 309 // Resize the normal screen buffer/history. 310 status_t error = BasicTerminalBuffer::ResizeTo(width, height, 311 historyCapacity); 312 if (error != B_OK) { 313 if (alternateScreenActive) 314 _SwitchScreenBuffer(); 315 return error; 316 } 317 318 // Switch to the alternate screen buffer and resize it. 319 if (fAlternateScreen != NULL) { 320 TermPos cursor = fCursor; 321 fCursor.SetTo(0, 0); 322 fWidth = oldWidth; 323 fHeight = oldHeight; 324 325 _SwitchScreenBuffer(); 326 327 error = BasicTerminalBuffer::ResizeTo(width, height, 0); 328 329 fWidth = width; 330 fHeight = height; 331 fCursor = cursor; 332 333 // Switch back. 334 if (!alternateScreenActive) 335 _SwitchScreenBuffer(); 336 337 if (error != B_OK) { 338 // This sucks -- we can't do anything about it. Delete the 339 // alternate screen buffer. 340 _FreeLines(fAlternateScreen, oldHeight); 341 fAlternateScreen = NULL; 342 } 343 } 344 345 return error; 346 } 347 348 349 void 350 TerminalBuffer::UseAlternateScreenBuffer(bool clear) 351 { 352 if (fAlternateScreenActive || fAlternateScreen == NULL) 353 return; 354 355 _SwitchScreenBuffer(); 356 357 if (clear) 358 Clear(false); 359 360 _InvalidateAll(); 361 } 362 363 364 void 365 TerminalBuffer::UseNormalScreenBuffer() 366 { 367 if (!fAlternateScreenActive) 368 return; 369 370 _SwitchScreenBuffer(); 371 _InvalidateAll(); 372 } 373 374 375 void 376 TerminalBuffer::_SwitchScreenBuffer() 377 { 378 std::swap(fScreen, fAlternateScreen); 379 std::swap(fHistory, fAlternateHistory); 380 std::swap(fScreenOffset, fAlternateScreenOffset); 381 std::swap(fAttributes, fAlternateAttributes); 382 fAlternateScreenActive = !fAlternateScreenActive; 383 } 384