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 if (index < kTermColorCount) 235 fColorsPalette[index] = color; 236 } 237 238 239 rgb_color 240 TerminalBuffer::PaletteColor(uint8 index) 241 { 242 return fColorsPalette[min_c(index, kTermColorCount - 1)]; 243 } 244 245 246 int 247 TerminalBuffer::GuessPaletteColor(int red, int green, int blue) 248 { 249 int distance = 255 * 100; 250 int index = -1; 251 for (uint32 i = 0; i < kTermColorCount && distance > 0; i++) { 252 rgb_color color = fColorsPalette[i]; 253 int r = 30 * abs(color.red - red); 254 int g = 59 * abs(color.green - green); 255 int b = 11 * abs(color.blue - blue); 256 int d = r + g + b; 257 if (distance > d) { 258 index = i; 259 distance = d; 260 } 261 } 262 263 return min_c(index, int(kTermColorCount - 1)); 264 } 265 266 267 void 268 TerminalBuffer::NotifyQuit(int32 reason) 269 { 270 if (fListenerValid) { 271 BMessage message(MSG_QUIT_TERMNAL); 272 message.AddInt32("reason", reason); 273 fListener.SendMessage(&message); 274 } 275 } 276 277 278 void 279 TerminalBuffer::NotifyListener() 280 { 281 if (fListenerValid) 282 fListener.SendMessage(MSG_TERMINAL_BUFFER_CHANGED); 283 } 284 285 286 status_t 287 TerminalBuffer::ResizeTo(int32 width, int32 height) 288 { 289 int32 historyCapacity = 0; 290 if (!fAlternateScreenActive) 291 historyCapacity = HistoryCapacity(); 292 else if (fAlternateHistory != NULL) 293 historyCapacity = fAlternateHistory->Capacity(); 294 295 return ResizeTo(width, height, historyCapacity); 296 } 297 298 299 status_t 300 TerminalBuffer::ResizeTo(int32 width, int32 height, int32 historyCapacity) 301 { 302 // switch to the normal screen buffer first 303 bool alternateScreenActive = fAlternateScreenActive; 304 if (alternateScreenActive) 305 _SwitchScreenBuffer(); 306 307 int32 oldWidth = fWidth; 308 int32 oldHeight = fHeight; 309 310 // Resize the normal screen buffer/history. 311 status_t error = BasicTerminalBuffer::ResizeTo(width, height, 312 historyCapacity); 313 if (error != B_OK) { 314 if (alternateScreenActive) 315 _SwitchScreenBuffer(); 316 return error; 317 } 318 319 // Switch to the alternate screen buffer and resize it. 320 if (fAlternateScreen != NULL) { 321 TermPos cursor = fCursor; 322 fCursor.SetTo(0, 0); 323 fWidth = oldWidth; 324 fHeight = oldHeight; 325 326 _SwitchScreenBuffer(); 327 328 error = BasicTerminalBuffer::ResizeTo(width, height, 0); 329 330 fWidth = width; 331 fHeight = height; 332 fCursor = cursor; 333 334 // Switch back. 335 if (!alternateScreenActive) 336 _SwitchScreenBuffer(); 337 338 if (error != B_OK) { 339 // This sucks -- we can't do anything about it. Delete the 340 // alternate screen buffer. 341 _FreeLines(fAlternateScreen, oldHeight); 342 fAlternateScreen = NULL; 343 } 344 } 345 346 return error; 347 } 348 349 350 void 351 TerminalBuffer::UseAlternateScreenBuffer(bool clear) 352 { 353 if (fAlternateScreenActive || fAlternateScreen == NULL) 354 return; 355 356 _SwitchScreenBuffer(); 357 358 if (clear) 359 Clear(false); 360 361 _InvalidateAll(); 362 } 363 364 365 void 366 TerminalBuffer::UseNormalScreenBuffer() 367 { 368 if (!fAlternateScreenActive) 369 return; 370 371 _SwitchScreenBuffer(); 372 _InvalidateAll(); 373 } 374 375 376 void 377 TerminalBuffer::_SwitchScreenBuffer() 378 { 379 std::swap(fScreen, fAlternateScreen); 380 std::swap(fHistory, fAlternateHistory); 381 std::swap(fScreenOffset, fAlternateScreenOffset); 382 std::swap(fAttributes, fAlternateAttributes); 383 fAlternateScreenActive = !fAlternateScreenActive; 384 } 385