xref: /haiku/src/kits/interface/InterfaceDefs.cpp (revision c9ad965c81b08802fed0827fd1dd16f45297928a)
1 /*
2  * Copyright 2001-2009, Haiku, Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		DarkWyrm <bpmagic@columbus.rr.com>
7  *		Caz <turok2@currantbun.com>
8  *		Axel Dörfler, axeld@pinc-software.de
9  */
10 
11 
12 /*!	Global functions and variables for the Interface Kit */
13 
14 
15 #include <InterfaceDefs.h>
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 
21 #include <ControlLook.h>
22 #include <Font.h>
23 #include <Menu.h>
24 #include <Roster.h>
25 #include <ScrollBar.h>
26 #include <Screen.h>
27 #include <String.h>
28 #include <TextView.h>
29 
30 #include <ApplicationPrivate.h>
31 #include <AppServerLink.h>
32 #include <ColorConversion.h>
33 #include <DefaultColors.h>
34 #include <InputServerTypes.h>
35 #include <input_globals.h>
36 #include <InterfacePrivate.h>
37 #include <MenuPrivate.h>
38 #include <pr_server.h>
39 #include <ServerProtocol.h>
40 #include <ServerReadOnlyMemory.h>
41 #include <truncate_string.h>
42 #include <utf8_functions.h>
43 #include <WidthBuffer.h>
44 #include <WindowInfo.h>
45 
46 
47 using namespace BPrivate;
48 
49 // some other weird struct exported by BeOS, it's not initialized, though
50 struct general_ui_info {
51 	rgb_color	background_color;
52 	rgb_color	mark_color;
53 	rgb_color	highlight_color;
54 	bool		color_frame;
55 	rgb_color	window_frame_color;
56 };
57 
58 struct general_ui_info general_info;
59 
60 menu_info *_menu_info_ptr_;
61 
62 extern "C" const char B_NOTIFICATION_SENDER[] = "be:sender";
63 
64 static const rgb_color _kDefaultColors[kNumColors] = {
65 	{216, 216, 216, 255},	// B_PANEL_BACKGROUND_COLOR
66 	{216, 216, 216, 255},	// B_MENU_BACKGROUND_COLOR
67 	{255, 203, 0, 255},		// B_WINDOW_TAB_COLOR
68 	{0, 0, 229, 255},		// B_KEYBOARD_NAVIGATION_COLOR
69 	{51, 102, 152, 255},	// B_DESKTOP_COLOR
70 	{153, 153, 153, 255},	// B_MENU_SELECTED_BACKGROUND_COLOR
71 	{0, 0, 0, 255},			// B_MENU_ITEM_TEXT_COLOR
72 	{0, 0, 0, 255},			// B_MENU_SELECTED_ITEM_TEXT_COLOR
73 	{0, 0, 0, 255},			// B_MENU_SELECTED_BORDER_COLOR
74 	{0, 0, 0, 255},			// B_PANEL_TEXT_COLOR
75 	{255, 255, 255, 255},	// B_DOCUMENT_BACKGROUND_COLOR
76 	{0, 0, 0, 255},			// B_DOCUMENT_TEXT_COLOR
77 	{245, 245, 245, 255},	// B_CONTROL_BACKGROUND_COLOR
78 	{0, 0, 0, 255},			// B_CONTROL_TEXT_COLOR
79 	{0, 0, 0, 255},			// B_CONTROL_BORDER_COLOR
80 	{102, 152, 203, 255},	// B_CONTROL_HIGHLIGHT_COLOR
81 	{0, 0, 0, 255},			// B_NAVIGATION_PULSE_COLOR
82 	{255, 255, 255, 255},	// B_SHINE_COLOR
83 	{0, 0, 0, 255},			// B_SHADOW_COLOR
84 	{255, 255, 216, 255},	// B_TOOLTIP_BACKGROUND_COLOR
85 	{0, 0, 0, 255},			// B_TOOLTIP_TEXT_COLOR
86 	{0, 0, 0, 255},			// B_WINDOW_TEXT_COLOR
87 	{232, 232, 232, 255},	// B_WINDOW_INACTIVE_TAB_COLOR
88 	{80, 80, 80, 255},		// B_WINDOW_INACTIVE_TEXT_COLOR
89 	// 100...
90 	{0, 255, 0, 255},		// B_SUCCESS_COLOR
91 	{255, 0, 0, 255},		// B_FAILURE_COLOR
92 	{}
93 };
94 const rgb_color* BPrivate::kDefaultColors = &_kDefaultColors[0];
95 
96 
97 namespace BPrivate {
98 
99 
100 /*!	Fills the \a width, \a height, and \a colorSpace parameters according
101 	to the window screen's mode.
102 	Returns \c true if the mode is known.
103 */
104 bool
105 get_mode_parameter(uint32 mode, int32& width, int32& height,
106 	uint32& colorSpace)
107 {
108 	switch (mode) {
109 		case B_8_BIT_640x480:
110 		case B_8_BIT_800x600:
111 		case B_8_BIT_1024x768:
112 		case B_8_BIT_1152x900:
113 		case B_8_BIT_1280x1024:
114 		case B_8_BIT_1600x1200:
115 			colorSpace = B_CMAP8;
116 			break;
117 
118 		case B_15_BIT_640x480:
119 		case B_15_BIT_800x600:
120 		case B_15_BIT_1024x768:
121 		case B_15_BIT_1152x900:
122 		case B_15_BIT_1280x1024:
123 		case B_15_BIT_1600x1200:
124 			colorSpace = B_RGB15;
125 			break;
126 
127 		case B_16_BIT_640x480:
128 		case B_16_BIT_800x600:
129 		case B_16_BIT_1024x768:
130 		case B_16_BIT_1152x900:
131 		case B_16_BIT_1280x1024:
132 		case B_16_BIT_1600x1200:
133 			colorSpace = B_RGB16;
134 			break;
135 
136 		case B_32_BIT_640x480:
137 		case B_32_BIT_800x600:
138 		case B_32_BIT_1024x768:
139 		case B_32_BIT_1152x900:
140 		case B_32_BIT_1280x1024:
141 		case B_32_BIT_1600x1200:
142 			colorSpace = B_RGB32;
143 			break;
144 
145 		default:
146 			return false;
147 	}
148 
149 	switch (mode) {
150 		case B_8_BIT_640x480:
151 		case B_15_BIT_640x480:
152 		case B_16_BIT_640x480:
153 		case B_32_BIT_640x480:
154 			width = 640; height = 480;
155 			break;
156 
157 		case B_8_BIT_800x600:
158 		case B_15_BIT_800x600:
159 		case B_16_BIT_800x600:
160 		case B_32_BIT_800x600:
161 			width = 800; height = 600;
162 			break;
163 
164 		case B_8_BIT_1024x768:
165 		case B_15_BIT_1024x768:
166 		case B_16_BIT_1024x768:
167 		case B_32_BIT_1024x768:
168 			width = 1024; height = 768;
169 			break;
170 
171 		case B_8_BIT_1152x900:
172 		case B_15_BIT_1152x900:
173 		case B_16_BIT_1152x900:
174 		case B_32_BIT_1152x900:
175 			width = 1152; height = 900;
176 			break;
177 
178 		case B_8_BIT_1280x1024:
179 		case B_15_BIT_1280x1024:
180 		case B_16_BIT_1280x1024:
181 		case B_32_BIT_1280x1024:
182 			width = 1280; height = 1024;
183 			break;
184 
185 		case B_8_BIT_1600x1200:
186 		case B_15_BIT_1600x1200:
187 		case B_16_BIT_1600x1200:
188 		case B_32_BIT_1600x1200:
189 			width = 1600; height = 1200;
190 			break;
191 	}
192 
193 	return true;
194 }
195 
196 
197 void
198 get_workspaces_layout(uint32* _columns, uint32* _rows)
199 {
200 	int32 columns = 1;
201 	int32 rows = 1;
202 
203 	BPrivate::AppServerLink link;
204 	link.StartMessage(AS_GET_WORKSPACE_LAYOUT);
205 
206 	status_t status;
207 	if (link.FlushWithReply(status) == B_OK && status == B_OK) {
208 		link.Read<int32>(&columns);
209 		link.Read<int32>(&rows);
210 	}
211 
212 	if (_columns != NULL)
213 		*_columns = columns;
214 	if (_rows != NULL)
215 		*_rows = rows;
216 }
217 
218 
219 void
220 set_workspaces_layout(uint32 columns, uint32 rows)
221 {
222 	if (columns < 1 || rows < 1)
223 		return;
224 
225 	BPrivate::AppServerLink link;
226 	link.StartMessage(AS_SET_WORKSPACE_LAYOUT);
227 	link.Attach<int32>(columns);
228 	link.Attach<int32>(rows);
229 	link.Flush();
230 }
231 
232 
233 }	// namespace BPrivate
234 
235 
236 void
237 set_subpixel_antialiasing(bool subpix)
238 {
239 	BPrivate::AppServerLink link;
240 
241 	link.StartMessage(AS_SET_SUBPIXEL_ANTIALIASING);
242 	link.Attach<bool>(subpix);
243 	link.Flush();
244 }
245 
246 
247 status_t
248 get_subpixel_antialiasing(bool* subpix)
249 {
250 	BPrivate::AppServerLink link;
251 
252 	link.StartMessage(AS_GET_SUBPIXEL_ANTIALIASING);
253 	int32 status = B_ERROR;
254 	if (link.FlushWithReply(status) != B_OK || status < B_OK)
255 		return status;
256 	link.Read<bool>(subpix);
257 	return B_OK;
258 }
259 
260 
261 void
262 set_hinting_mode(uint8 hinting)
263 {
264 	BPrivate::AppServerLink link;
265 
266 	link.StartMessage(AS_SET_HINTING);
267 	link.Attach<uint8>(hinting);
268 	link.Flush();
269 }
270 
271 
272 status_t
273 get_hinting_mode(uint8* hinting)
274 {
275 	BPrivate::AppServerLink link;
276 
277 	link.StartMessage(AS_GET_HINTING);
278 	int32 status = B_ERROR;
279 	if (link.FlushWithReply(status) != B_OK || status < B_OK)
280 		return status;
281 	link.Read<uint8>(hinting);
282 	return B_OK;
283 }
284 
285 
286 void
287 set_average_weight(uint8 averageWeight)
288 {
289 	BPrivate::AppServerLink link;
290 
291 	link.StartMessage(AS_SET_SUBPIXEL_AVERAGE_WEIGHT);
292 	link.Attach<uint8>(averageWeight);
293 	link.Flush();
294 }
295 
296 
297 status_t
298 get_average_weight(uint8* averageWeight)
299 {
300 	BPrivate::AppServerLink link;
301 
302 	link.StartMessage(AS_GET_SUBPIXEL_AVERAGE_WEIGHT);
303 	int32 status = B_ERROR;
304 	if (link.FlushWithReply(status) != B_OK || status < B_OK)
305 		return status;
306 	link.Read<uint8>(averageWeight);
307 	return B_OK;
308 }
309 
310 
311 void
312 set_is_subpixel_ordering_regular(bool subpixelOrdering)
313 {
314 	BPrivate::AppServerLink link;
315 
316 	link.StartMessage(AS_SET_SUBPIXEL_ORDERING);
317 	link.Attach<bool>(subpixelOrdering);
318 	link.Flush();
319 }
320 
321 
322 status_t
323 get_is_subpixel_ordering_regular(bool* subpixelOrdering)
324 {
325 	BPrivate::AppServerLink link;
326 
327 	link.StartMessage(AS_GET_SUBPIXEL_ORDERING);
328 	int32 status = B_ERROR;
329 	if (link.FlushWithReply(status) != B_OK || status < B_OK)
330 		return status;
331 	link.Read<bool>(subpixelOrdering);
332 	return B_OK;
333 }
334 
335 
336 const color_map *
337 system_colors()
338 {
339 	return BScreen(B_MAIN_SCREEN_ID).ColorMap();
340 }
341 
342 
343 status_t
344 set_screen_space(int32 index, uint32 space, bool stick)
345 {
346 	int32 width;
347 	int32 height;
348 	uint32 depth;
349 	if (!BPrivate::get_mode_parameter(space, width, height, depth))
350 		return B_BAD_VALUE;
351 
352 	BScreen screen(B_MAIN_SCREEN_ID);
353 	display_mode mode;
354 
355 	// TODO: What about refresh rate ?
356 	// currently we get it from the current video mode, but
357 	// this might be not so wise.
358 	status_t status = screen.GetMode(index, &mode);
359 	if (status < B_OK)
360 		return status;
361 
362 	mode.virtual_width = width;
363 	mode.virtual_height = height;
364 	mode.space = depth;
365 
366 	return screen.SetMode(index, &mode, stick);
367 }
368 
369 
370 status_t
371 get_scroll_bar_info(scroll_bar_info *info)
372 {
373 	if (info == NULL)
374 		return B_BAD_VALUE;
375 
376 	BPrivate::AppServerLink link;
377 	link.StartMessage(AS_GET_SCROLLBAR_INFO);
378 
379 	int32 code;
380 	if (link.FlushWithReply(code) == B_OK
381 		&& code == B_OK) {
382 		link.Read<scroll_bar_info>(info);
383 		return B_OK;
384 	}
385 
386 	return B_ERROR;
387 }
388 
389 
390 status_t
391 set_scroll_bar_info(scroll_bar_info *info)
392 {
393 	if (info == NULL)
394 		return B_BAD_VALUE;
395 
396 	BPrivate::AppServerLink link;
397 	int32 code;
398 
399 	link.StartMessage(AS_SET_SCROLLBAR_INFO);
400 	link.Attach<scroll_bar_info>(*info);
401 
402 	if (link.FlushWithReply(code) == B_OK
403 		&& code == B_OK)
404 		return B_OK;
405 
406 	return B_ERROR;
407 }
408 
409 
410 status_t
411 get_mouse_type(int32 *type)
412 {
413 	BMessage command(IS_GET_MOUSE_TYPE);
414 	BMessage reply;
415 
416 	status_t err = _control_input_server_(&command, &reply);
417 	if (err != B_OK)
418 		return err;
419 	return reply.FindInt32("mouse_type", type);
420 }
421 
422 
423 status_t
424 set_mouse_type(int32 type)
425 {
426 	BMessage command(IS_SET_MOUSE_TYPE);
427 	BMessage reply;
428 
429 	status_t err = command.AddInt32("mouse_type", type);
430 	if (err != B_OK)
431 		return err;
432 	return _control_input_server_(&command, &reply);
433 }
434 
435 
436 status_t
437 get_mouse_map(mouse_map *map)
438 {
439 	BMessage command(IS_GET_MOUSE_MAP);
440 	BMessage reply;
441 	const void *data = 0;
442 	ssize_t count;
443 
444 	status_t err = _control_input_server_(&command, &reply);
445 	if (err == B_OK)
446 		err = reply.FindData("mousemap", B_RAW_TYPE, &data, &count);
447 	if (err != B_OK)
448 		return err;
449 
450 	memcpy(map, data, count);
451 
452 	return B_OK;
453 }
454 
455 
456 status_t
457 set_mouse_map(mouse_map *map)
458 {
459 	BMessage command(IS_SET_MOUSE_MAP);
460 	BMessage reply;
461 
462 	status_t err = command.AddData("mousemap", B_RAW_TYPE, map,
463 		sizeof(mouse_map));
464 	if (err != B_OK)
465 		return err;
466 	return _control_input_server_(&command, &reply);
467 }
468 
469 
470 status_t
471 get_click_speed(bigtime_t *speed)
472 {
473 	BMessage command(IS_GET_CLICK_SPEED);
474 	BMessage reply;
475 
476 	status_t err = _control_input_server_(&command, &reply);
477 	if (err != B_OK)
478 		return err;
479 
480 	if (reply.FindInt64("speed", speed) != B_OK)
481 		*speed = 500000;
482 
483 	return B_OK;
484 }
485 
486 
487 status_t
488 set_click_speed(bigtime_t speed)
489 {
490 	BMessage command(IS_SET_CLICK_SPEED);
491 	BMessage reply;
492 	command.AddInt64("speed", speed);
493 	return _control_input_server_(&command, &reply);
494 }
495 
496 
497 status_t
498 get_mouse_speed(int32 *speed)
499 {
500 	BMessage command(IS_GET_MOUSE_SPEED);
501 	BMessage reply;
502 
503 	status_t err = _control_input_server_(&command, &reply);
504 	if (err != B_OK)
505 		return err;
506 
507 	if (reply.FindInt32("speed", speed) != B_OK)
508 		*speed = 65536;
509 
510 	return B_OK;
511 }
512 
513 
514 status_t
515 set_mouse_speed(int32 speed)
516 {
517 	BMessage command(IS_SET_MOUSE_SPEED);
518 	BMessage reply;
519 	command.AddInt32("speed", speed);
520 	return _control_input_server_(&command, &reply);
521 }
522 
523 
524 status_t
525 get_mouse_acceleration(int32 *speed)
526 {
527 	BMessage command(IS_GET_MOUSE_ACCELERATION);
528 	BMessage reply;
529 
530 	_control_input_server_(&command, &reply);
531 
532 	if (reply.FindInt32("speed", speed) != B_OK)
533 		*speed = 65536;
534 
535 	return B_OK;
536 }
537 
538 
539 status_t
540 set_mouse_acceleration(int32 speed)
541 {
542 	BMessage command(IS_SET_MOUSE_ACCELERATION);
543 	BMessage reply;
544 	command.AddInt32("speed", speed);
545 	return _control_input_server_(&command, &reply);
546 }
547 
548 
549 status_t
550 get_key_repeat_rate(int32 *rate)
551 {
552 	BMessage command(IS_GET_KEY_REPEAT_RATE);
553 	BMessage reply;
554 
555 	_control_input_server_(&command, &reply);
556 
557 	if (reply.FindInt32("rate", rate) != B_OK)
558 		*rate = 250000;
559 
560 	return B_OK;
561 }
562 
563 
564 status_t
565 set_key_repeat_rate(int32 rate)
566 {
567 	BMessage command(IS_SET_KEY_REPEAT_RATE);
568 	BMessage reply;
569 	command.AddInt32("rate", rate);
570 	return _control_input_server_(&command, &reply);
571 }
572 
573 
574 status_t
575 get_key_repeat_delay(bigtime_t *delay)
576 {
577 	BMessage command(IS_GET_KEY_REPEAT_DELAY);
578 	BMessage reply;
579 
580 	_control_input_server_(&command, &reply);
581 
582 	if (reply.FindInt64("delay", delay) != B_OK)
583 		*delay = 200;
584 
585 	return B_OK;
586 }
587 
588 
589 status_t
590 set_key_repeat_delay(bigtime_t  delay)
591 {
592 	BMessage command(IS_SET_KEY_REPEAT_DELAY);
593 	BMessage reply;
594 	command.AddInt64("delay", delay);
595 	return _control_input_server_(&command, &reply);
596 }
597 
598 
599 uint32
600 modifiers()
601 {
602 	BMessage command(IS_GET_MODIFIERS);
603 	BMessage reply;
604 	int32 err, modifier;
605 
606 	_control_input_server_(&command, &reply);
607 
608 	if (reply.FindInt32("status", &err) != B_OK)
609 		return 0;
610 
611 	if (reply.FindInt32("modifiers", &modifier) != B_OK)
612 		return 0;
613 
614 	return modifier;
615 }
616 
617 
618 status_t
619 get_key_info(key_info *info)
620 {
621 	BMessage command(IS_GET_KEY_INFO);
622 	BMessage reply;
623 	const void *data = 0;
624 	int32 err;
625 	ssize_t count;
626 
627 	_control_input_server_(&command, &reply);
628 
629 	if (reply.FindInt32("status", &err) != B_OK)
630 		return B_ERROR;
631 
632 	if (reply.FindData("key_info", B_ANY_TYPE, &data, &count) != B_OK)
633 		return B_ERROR;
634 
635 	memcpy(info, data, count);
636 	return B_OK;
637 }
638 
639 
640 void
641 get_key_map(key_map **map, char **key_buffer)
642 {
643 	_get_key_map(map, key_buffer, NULL);
644 }
645 
646 
647 void
648 _get_key_map(key_map **map, char **key_buffer, ssize_t *key_buffer_size)
649 {
650 	BMessage command(IS_GET_KEY_MAP);
651 	BMessage reply;
652 	ssize_t map_count, key_count;
653 	const void *map_array = 0, *key_array = 0;
654 	if (key_buffer_size == NULL)
655 		key_buffer_size = &key_count;
656 
657 	_control_input_server_(&command, &reply);
658 
659 	if (reply.FindData("keymap", B_ANY_TYPE, &map_array, &map_count) != B_OK) {
660 		*map = 0; *key_buffer = 0;
661 		return;
662 	}
663 
664 	if (reply.FindData("key_buffer", B_ANY_TYPE, &key_array, key_buffer_size)
665 			!= B_OK) {
666 		*map = 0; *key_buffer = 0;
667 		return;
668 	}
669 
670 	*map = (key_map *)malloc(map_count);
671 	memcpy(*map, map_array, map_count);
672 	*key_buffer = (char *)malloc(*key_buffer_size);
673 	memcpy(*key_buffer, key_array, *key_buffer_size);
674 }
675 
676 
677 status_t
678 get_keyboard_id(uint16 *id)
679 {
680 	BMessage command(IS_GET_KEYBOARD_ID);
681 	BMessage reply;
682 	uint16 kid;
683 
684 	_control_input_server_(&command, &reply);
685 
686 	status_t err = reply.FindInt16("id", (int16 *)&kid);
687 	if (err != B_OK)
688 		return err;
689 	*id = kid;
690 
691 	return B_OK;
692 }
693 
694 
695 status_t
696 get_modifier_key(uint32 modifier, uint32 *key)
697 {
698 	BMessage command(IS_GET_MODIFIER_KEY);
699 	BMessage reply;
700 	uint32 rkey;
701 
702 	command.AddInt32("modifier", modifier);
703 	_control_input_server_(&command, &reply);
704 
705 	status_t err = reply.FindInt32("key", (int32 *) &rkey);
706 	if (err != B_OK)
707 		return err;
708 	*key = rkey;
709 
710 	return B_OK;
711 }
712 
713 
714 void
715 set_modifier_key(uint32 modifier, uint32 key)
716 {
717 	BMessage command(IS_SET_MODIFIER_KEY);
718 	BMessage reply;
719 
720 	command.AddInt32("modifier", modifier);
721 	command.AddInt32("key", key);
722 	_control_input_server_(&command, &reply);
723 }
724 
725 
726 void
727 set_keyboard_locks(uint32 modifiers)
728 {
729 	BMessage command(IS_SET_KEYBOARD_LOCKS);
730 	BMessage reply;
731 
732 	command.AddInt32("locks", modifiers);
733 	_control_input_server_(&command, &reply);
734 }
735 
736 
737 status_t
738 _restore_key_map_()
739 {
740 	BMessage message(IS_RESTORE_KEY_MAP);
741 	BMessage reply;
742 
743 	return _control_input_server_(&message, &reply);
744 }
745 
746 
747 rgb_color
748 keyboard_navigation_color()
749 {
750 	// Queries the app_server
751 	return ui_color(B_KEYBOARD_NAVIGATION_COLOR);
752 }
753 
754 
755 int32
756 count_workspaces()
757 {
758 	uint32 columns;
759 	uint32 rows;
760 	BPrivate::get_workspaces_layout(&columns, &rows);
761 
762 	return columns * rows;
763 }
764 
765 
766 void
767 set_workspace_count(int32 count)
768 {
769 	int32 squareRoot = (int32)sqrt(count);
770 
771 	int32 rows = 1;
772 	for (int32 i = 2; i <= squareRoot; i++) {
773 		if (count % i == 0)
774 			rows = i;
775 	}
776 
777 	int32 columns = count / rows;
778 
779 	BPrivate::set_workspaces_layout(columns, rows);
780 }
781 
782 
783 int32
784 current_workspace()
785 {
786 	int32 index = 0;
787 
788 	BPrivate::AppServerLink link;
789 	link.StartMessage(AS_CURRENT_WORKSPACE);
790 
791 	int32 status;
792 	if (link.FlushWithReply(status) == B_OK && status == B_OK)
793 		link.Read<int32>(&index);
794 
795 	return index;
796 }
797 
798 
799 void
800 activate_workspace(int32 workspace)
801 {
802 	BPrivate::AppServerLink link;
803 	link.StartMessage(AS_ACTIVATE_WORKSPACE);
804 	link.Attach<int32>(workspace);
805 	link.Flush();
806 }
807 
808 
809 bigtime_t
810 idle_time()
811 {
812 	bigtime_t idletime = 0;
813 
814 	BPrivate::AppServerLink link;
815 	link.StartMessage(AS_IDLE_TIME);
816 
817 	int32 code;
818 	if (link.FlushWithReply(code) == B_OK && code == B_OK)
819 		link.Read<int64>(&idletime);
820 
821 	return idletime;
822 }
823 
824 
825 void
826 run_select_printer_panel()
827 {
828 	if (be_roster == NULL)
829 		return;
830 
831 	// Launches the Printer prefs app via the Roster
832 	be_roster->Launch(PRNT_SIGNATURE_TYPE);
833 }
834 
835 
836 void
837 run_add_printer_panel()
838 {
839 	// Launches the Printer prefs app via the Roster and asks it to
840 	// add a printer
841 	run_select_printer_panel();
842 
843 	BMessenger printerPanelMessenger(PRNT_SIGNATURE_TYPE);
844 	printerPanelMessenger.SendMessage(PRINTERS_ADD_PRINTER);
845 }
846 
847 
848 void
849 run_be_about()
850 {
851 	if (be_roster != NULL)
852 		be_roster->Launch("application/x-vnd.Haiku-About");
853 }
854 
855 
856 void
857 set_focus_follows_mouse(bool follow)
858 {
859 	// obviously deprecated API
860 	set_mouse_mode(B_FOCUS_FOLLOWS_MOUSE);
861 }
862 
863 
864 bool
865 focus_follows_mouse()
866 {
867 	return mouse_mode() == B_FOCUS_FOLLOWS_MOUSE;
868 }
869 
870 
871 void
872 set_mouse_mode(mode_mouse mode)
873 {
874 	BPrivate::AppServerLink link;
875 	link.StartMessage(AS_SET_MOUSE_MODE);
876 	link.Attach<mode_mouse>(mode);
877 	link.Flush();
878 }
879 
880 
881 mode_mouse
882 mouse_mode()
883 {
884 	// Gets the mouse focus style, such as activate to click,
885 	// focus to click, ...
886 	mode_mouse mode = B_NORMAL_MOUSE;
887 
888 	BPrivate::AppServerLink link;
889 	link.StartMessage(AS_GET_MOUSE_MODE);
890 
891 	int32 code;
892 	if (link.FlushWithReply(code) == B_OK && code == B_OK)
893 		link.Read<mode_mouse>(&mode);
894 
895 	return mode;
896 }
897 
898 
899 void
900 set_focus_follows_mouse_mode(mode_focus_follows_mouse mode)
901 {
902 	BPrivate::AppServerLink link;
903 	link.StartMessage(AS_SET_FOCUS_FOLLOWS_MOUSE_MODE);
904 	link.Attach<mode_focus_follows_mouse>(mode);
905 	link.Flush();
906 }
907 
908 
909 mode_focus_follows_mouse
910 focus_follows_mouse_mode()
911 {
912 	mode_focus_follows_mouse mode = B_NORMAL_FOCUS_FOLLOWS_MOUSE;
913 
914 	BPrivate::AppServerLink link;
915 	link.StartMessage(AS_GET_FOCUS_FOLLOWS_MOUSE_MODE);
916 
917 	int32 code;
918 	if (link.FlushWithReply(code) == B_OK && code == B_OK)
919 		link.Read<mode_focus_follows_mouse>(&mode);
920 
921 	return mode;
922 }
923 
924 
925 void
926 set_accept_first_click(bool acceptFirstClick)
927 {
928 	BPrivate::AppServerLink link;
929 	link.StartMessage(AS_SET_ACCEPT_FIRST_CLICK);
930 	link.Attach<bool>(acceptFirstClick);
931 	link.Flush();
932 }
933 
934 
935 bool
936 accept_first_click()
937 {
938 	// Gets the accept first click status
939 	bool acceptFirstClick = false;
940 
941 	BPrivate::AppServerLink link;
942 	link.StartMessage(AS_GET_ACCEPT_FIRST_CLICK);
943 
944 	int32 code;
945 	if (link.FlushWithReply(code) == B_OK && code == B_OK)
946 		link.Read<bool>(&acceptFirstClick);
947 
948 	return acceptFirstClick;
949 }
950 
951 
952 rgb_color
953 ui_color(color_which which)
954 {
955 	int32 index = color_which_to_index(which);
956 	if (index < 0 || index >= kNumColors) {
957 		fprintf(stderr, "ui_color(): unknown color_which %d\n", which);
958 		return make_color(0, 0, 0);
959 	}
960 
961 	if (be_app) {
962 		server_read_only_memory* shared
963 			= BApplication::Private::ServerReadOnlyMemory();
964 		return shared->colors[index];
965 	}
966 
967 	return kDefaultColors[index];
968 }
969 
970 
971 void
972 set_ui_color(const color_which &which, const rgb_color &color)
973 {
974 	int32 index = color_which_to_index(which);
975 	if (index < 0 || index >= kNumColors) {
976 		fprintf(stderr, "set_ui_color(): unknown color_which %d\n", which);
977 		return;
978 	}
979 
980 	BPrivate::AppServerLink link;
981 	link.StartMessage(AS_SET_UI_COLOR);
982 	link.Attach<color_which>(which);
983 	link.Attach<rgb_color>(color);
984 	link.Flush();
985 }
986 
987 
988 rgb_color
989 tint_color(rgb_color color, float tint)
990 {
991 	rgb_color result;
992 
993 	#define LIGHTEN(x) ((uint8)(255.0f - (255.0f - x) * tint))
994 	#define DARKEN(x)  ((uint8)(x * (2 - tint)))
995 
996 	if (tint < 1.0f) {
997 		result.red   = LIGHTEN(color.red);
998 		result.green = LIGHTEN(color.green);
999 		result.blue  = LIGHTEN(color.blue);
1000 		result.alpha = color.alpha;
1001 	} else {
1002 		result.red   = DARKEN(color.red);
1003 		result.green = DARKEN(color.green);
1004 		result.blue  = DARKEN(color.blue);
1005 		result.alpha = color.alpha;
1006 	}
1007 
1008 	#undef LIGHTEN
1009 	#undef DARKEN
1010 
1011 	return result;
1012 }
1013 
1014 
1015 rgb_color shift_color(rgb_color color, float shift);
1016 
1017 rgb_color
1018 shift_color(rgb_color color, float shift)
1019 {
1020 	return tint_color(color, shift);
1021 }
1022 
1023 
1024 extern "C" status_t
1025 _init_interface_kit_()
1026 {
1027 	status_t status = BPrivate::PaletteConverter::InitializeDefault(true);
1028 	if (status < B_OK)
1029 		return status;
1030 
1031 	// TODO: Could support different themes here in the future.
1032 	be_control_look = new BControlLook();
1033 
1034 	_init_global_fonts_();
1035 
1036 	BPrivate::gWidthBuffer = new BPrivate::WidthBuffer;
1037 	status = BPrivate::MenuPrivate::CreateBitmaps();
1038 	if (status != B_OK)
1039 		return status;
1040 
1041 	_menu_info_ptr_ = &BMenu::sMenuInfo;
1042 
1043 	status = get_menu_info(&BMenu::sMenuInfo);
1044 	if (status != B_OK)
1045 		return status;
1046 
1047 	general_info.background_color = ui_color(B_PANEL_BACKGROUND_COLOR);
1048 	general_info.mark_color.set_to(0, 0, 0);
1049 	general_info.highlight_color = ui_color(B_CONTROL_HIGHLIGHT_COLOR);
1050 	general_info.window_frame_color = ui_color(B_WINDOW_TAB_COLOR);
1051 	general_info.color_frame = true;
1052 
1053 	// TODO: fill the other static members
1054 
1055 	return status;
1056 }
1057 
1058 
1059 extern "C" status_t
1060 _fini_interface_kit_()
1061 {
1062 	BPrivate::MenuPrivate::DeleteBitmaps();
1063 
1064 	delete BPrivate::gWidthBuffer;
1065 	BPrivate::gWidthBuffer = NULL;
1066 
1067 	delete be_control_look;
1068 	be_control_look = NULL;
1069 
1070 	// TODO: Anything else?
1071 
1072 	return B_OK;
1073 }
1074 
1075 
1076 //	#pragma mark -
1077 
1078 
1079 /*!	\brief private function used by Deskbar to set window decor
1080 	Note, we don't have to be compatible here, and could just change
1081 	the Deskbar not to use this anymore
1082 	\param theme The theme to choose
1083 
1084 	- \c 0: BeOS
1085 	- \c 1: AmigaOS
1086 	- \c 2: Win95
1087 	- \c 3: MacOS
1088 */
1089 void
1090 __set_window_decor(int32 theme)
1091 {
1092 	BPrivate::AppServerLink link;
1093 	link.StartMessage(AS_R5_SET_DECORATOR);
1094 	link.Attach<int32>(theme);
1095 	link.Flush();
1096 }
1097 
1098 
1099 namespace BPrivate {
1100 
1101 
1102 /*!	\brief queries the server for the number of available decorators
1103 	\return the number of available decorators
1104 */
1105 int32
1106 count_decorators(void)
1107 {
1108 	BPrivate::AppServerLink link;
1109 	link.StartMessage(AS_COUNT_DECORATORS);
1110 
1111 	int32 code;
1112 	int32 count = -1;
1113 	if (link.FlushWithReply(code) == B_OK && code == B_OK)
1114 		link.Read<int32>(&count);
1115 
1116 	return count;
1117 }
1118 
1119 
1120 /*!	\brief queries the server for the index of the current decorators
1121 	\return the current decorator's index
1122 
1123 	If for some bizarre reason this function fails, it returns -1
1124 */
1125 int32
1126 get_decorator(void)
1127 {
1128 	BPrivate::AppServerLink link;
1129 	link.StartMessage(AS_GET_DECORATOR);
1130 
1131 	int32 code;
1132 	int32 index = -1;
1133 	if (link.FlushWithReply(code) == B_OK && code == B_OK)
1134 		link.Read<int32>(&index);
1135 
1136 	return index;
1137 }
1138 
1139 
1140 /*!	\brief queries the server for the name of the decorator with a certain index
1141 	\param index The index of the decorator to get the name for
1142 	\param name BString to receive the name of the decorator
1143 	\return B_OK if successful, B_ERROR if not
1144 */
1145 status_t
1146 get_decorator_name(const int32 &index, BString &name)
1147 {
1148 	BPrivate::AppServerLink link;
1149 	link.StartMessage(AS_GET_DECORATOR_NAME);
1150 	link.Attach<int32>(index);
1151 
1152 	int32 code;
1153 	if (link.FlushWithReply(code) == B_OK && code == B_OK) {
1154 		char *string;
1155 		if (link.ReadString(&string) == B_OK) {
1156 			name = string;
1157 			free(string);
1158 			return B_OK;
1159 		}
1160 	}
1161 
1162 	return B_ERROR;
1163 }
1164 
1165 
1166 /*!	\brief asks the server to draw a decorator preview into a BBitmap
1167 	\param index The index of the decorator to get the name for
1168 	\param bitmap BBitmap to receive the preview
1169 	\return B_OK if successful, B_ERROR if not.
1170 
1171 	This is currently unimplemented.
1172 */
1173 status_t
1174 get_decorator_preview(const int32 &index, BBitmap *bitmap)
1175 {
1176 	// TODO: implement get_decorator_preview
1177 	return B_ERROR;
1178 }
1179 
1180 
1181 /*!	\brief Private function which sets the window decorator for the system.
1182 	\param index Index of the decorator to set
1183 
1184 	If the index is invalid, this function and the server do nothing
1185 */
1186 status_t
1187 set_decorator(const int32 &index)
1188 {
1189 	if (index < 0)
1190 		return B_BAD_VALUE;
1191 
1192 	BPrivate::AppServerLink link;
1193 
1194 	link.StartMessage(AS_SET_DECORATOR);
1195 	link.Attach<int32>(index);
1196 	link.Flush();
1197 
1198 	return B_OK;
1199 }
1200 
1201 
1202 status_t
1203 get_application_order(int32 workspace, team_id** _applications,
1204 	int32* _count)
1205 {
1206 	BPrivate::AppServerLink link;
1207 
1208 	link.StartMessage(AS_GET_APPLICATION_ORDER);
1209 	link.Attach<int32>(workspace);
1210 
1211 	int32 code;
1212 	status_t status = link.FlushWithReply(code);
1213 	if (status != B_OK)
1214 		return status;
1215 	if (code != B_OK)
1216 		return code;
1217 
1218 	int32 count;
1219 	link.Read<int32>(&count);
1220 
1221 	*_applications = (team_id*)malloc(count * sizeof(team_id));
1222 	if (*_applications == NULL)
1223 		return B_NO_MEMORY;
1224 
1225 	link.Read(*_applications, count * sizeof(team_id));
1226 	*_count = count;
1227 	return B_OK;
1228 }
1229 
1230 
1231 status_t
1232 get_window_order(int32 workspace, int32** _tokens, int32* _count)
1233 {
1234 	BPrivate::AppServerLink link;
1235 
1236 	link.StartMessage(AS_GET_WINDOW_ORDER);
1237 	link.Attach<int32>(workspace);
1238 
1239 	int32 code;
1240 	status_t status = link.FlushWithReply(code);
1241 	if (status != B_OK)
1242 		return status;
1243 	if (code != B_OK)
1244 		return code;
1245 
1246 	int32 count;
1247 	link.Read<int32>(&count);
1248 
1249 	*_tokens = (int32*)malloc(count * sizeof(int32));
1250 	if (*_tokens == NULL)
1251 		return B_NO_MEMORY;
1252 
1253 	link.Read(*_tokens, count * sizeof(int32));
1254 	*_count = count;
1255 	return B_OK;
1256 }
1257 
1258 
1259 }	// namespace BPrivate
1260 
1261 // These methods were marked with "Danger, will Robinson!" in
1262 // the OpenTracker source, so we might not want to be compatible
1263 // here.
1264 // In any way, we would need to update Deskbar to use our
1265 // replacements, so we could as well just implement them...
1266 
1267 void
1268 do_window_action(int32 windowToken, int32 action, BRect zoomRect, bool zoom)
1269 {
1270 	BPrivate::AppServerLink link;
1271 
1272 	link.StartMessage(AS_WINDOW_ACTION);
1273 	link.Attach<int32>(windowToken);
1274 	link.Attach<int32>(action);
1275 		// we don't have any zooming effect
1276 
1277 	link.Flush();
1278 }
1279 
1280 
1281 client_window_info*
1282 get_window_info(int32 serverToken)
1283 {
1284 	BPrivate::AppServerLink link;
1285 
1286 	link.StartMessage(AS_GET_WINDOW_INFO);
1287 	link.Attach<int32>(serverToken);
1288 
1289 	int32 code;
1290 	if (link.FlushWithReply(code) != B_OK || code != B_OK)
1291 		return NULL;
1292 
1293 	int32 size;
1294 	link.Read<int32>(&size);
1295 
1296 	client_window_info* info = (client_window_info*)malloc(size);
1297 	if (info == NULL)
1298 		return NULL;
1299 
1300 	link.Read(info, size);
1301 	return info;
1302 }
1303 
1304 
1305 int32*
1306 get_token_list(team_id team, int32* _count)
1307 {
1308 	BPrivate::AppServerLink link;
1309 
1310 	link.StartMessage(AS_GET_WINDOW_LIST);
1311 	link.Attach<team_id>(team);
1312 
1313 	int32 code;
1314 	if (link.FlushWithReply(code) != B_OK || code != B_OK)
1315 		return NULL;
1316 
1317 	int32 count;
1318 	link.Read<int32>(&count);
1319 
1320 	int32* tokens = (int32*)malloc(count * sizeof(int32));
1321 	if (tokens == NULL)
1322 		return NULL;
1323 
1324 	link.Read(tokens, count * sizeof(int32));
1325 	*_count = count;
1326 	return tokens;
1327 }
1328 
1329 
1330 void
1331 do_bring_to_front_team(BRect zoomRect, team_id team, bool zoom)
1332 {
1333 	BPrivate::AppServerLink link;
1334 
1335 	link.StartMessage(AS_BRING_TEAM_TO_FRONT);
1336 	link.Attach<team_id>(team);
1337 		// we don't have any zooming effect
1338 
1339 	link.Flush();
1340 }
1341 
1342 
1343 void
1344 do_minimize_team(BRect zoomRect, team_id team, bool zoom)
1345 {
1346 	BPrivate::AppServerLink link;
1347 
1348 	link.StartMessage(AS_MINIMIZE_TEAM);
1349 	link.Attach<team_id>(team);
1350 		// we don't have any zooming effect
1351 
1352 	link.Flush();
1353 }
1354 
1355 
1356 //	#pragma mark - truncate string
1357 
1358 
1359 static char*
1360 write_ellipsis(char* dst)
1361 {
1362 	strcpy(dst, B_UTF8_ELLIPSIS);
1363 	// The UTF-8 character spans over 3 bytes
1364 	return dst + 3;
1365 }
1366 
1367 
1368 static bool
1369 optional_char_fits(float escapement, float fontSize, float gap)
1370 {
1371 	const float size = escapement * fontSize;
1372 	if (size <= gap || fabs(size - gap) <= 0.0001)
1373 		return true;
1374 	return false;
1375 }
1376 
1377 
1378 bool
1379 truncate_end(const char* source, char* dest, uint32 numChars,
1380 	const float* escapementArray, float width, float ellipsisWidth, float size)
1381 {
1382 	float currentWidth = 0.0;
1383 	ellipsisWidth /= size;
1384 		// test if this is as accurate as escapementArray * size
1385 	width /= size;
1386 	uint32 lastFit = 0, c;
1387 
1388 	for (c = 0; c < numChars; c++) {
1389 		currentWidth += escapementArray[c];
1390 		if (currentWidth + ellipsisWidth <= width)
1391 			lastFit = c;
1392 
1393 		if (currentWidth > width)
1394 			break;
1395 	}
1396 
1397 	if (c == numChars) {
1398 		// string fits into width
1399 		return false;
1400 	}
1401 
1402 	if (c == 0) {
1403 		// there is no space for the ellipsis
1404 		strcpy(dest, "");
1405 		return true;
1406 	}
1407 
1408 	// copy string to destination
1409 
1410 	for (uint32 i = 0; i < lastFit + 1; i++) {
1411 		// copy one glyph
1412 		do {
1413 			*dest++ = *source++;
1414 		} while (IsInsideGlyph(*source));
1415 	}
1416 
1417 	// write ellipsis and terminate
1418 
1419 	dest = write_ellipsis(dest);
1420 	*dest = '\0';
1421 	return true;
1422 }
1423 
1424 
1425 static char*
1426 copy_from_end(const char* src, char* dst, uint32 numChars, uint32 length,
1427 	const float* escapementArray, float width, float ellipsisWidth, float size)
1428 {
1429 	const char* originalStart = src;
1430 	src += length - 1;
1431 	float currentWidth = 0.0;
1432 	for (int32 c = numChars - 1; c > 0; c--) {
1433 		currentWidth += escapementArray[c] * size;
1434 		if (currentWidth > width) {
1435 			// ups, we definitely don't fit. go back until the ellipsis fits
1436 			currentWidth += ellipsisWidth;
1437 			// go forward again until ellipsis fits (already beyond the target)
1438 			for (uint32 c2 = c; c2 < numChars; c2++) {
1439 //printf(" backward: %c (%ld) (%.1f - %.1f = %.1f)\n", *dst, c2, currentWidth, escapementArray[c2] * size,
1440 //	currentWidth - escapementArray[c2] * size);
1441 				currentWidth -= escapementArray[c2] * size;
1442 				do {
1443 					src++;
1444 				} while (IsInsideGlyph(*src));
1445 				// see if we went back enough
1446 				if (currentWidth <= width)
1447 					break;
1448 			}
1449 			break;
1450 		} else {
1451 			// go back one glyph
1452 			do {
1453 				src--;
1454 			} while (IsInsideGlyph(*src));
1455 		}
1456 	}
1457 	// copy from the end of the string
1458 	uint32 bytesToCopy = originalStart + length - src;
1459 	memcpy(dst, src, bytesToCopy);
1460 	dst += bytesToCopy;
1461 	return dst;
1462 }
1463 
1464 
1465 bool
1466 truncate_middle(const char* source, char* dest, uint32 numChars,
1467 	const float* escapementArray, float width, float ellipsisWidth, float size)
1468 {
1469 	float mid = (width - ellipsisWidth) / 2.0;
1470 
1471 	uint32 left = 0;
1472 	float leftWidth = 0.0;
1473 	while (left < numChars && (leftWidth + (escapementArray[left] * size))
1474 			< mid) {
1475 		leftWidth += (escapementArray[left++] * size);
1476 	}
1477 
1478 	if (left == numChars)
1479 		return false;
1480 
1481 	float rightWidth = 0.0;
1482 	uint32 right = numChars;
1483 	while (right > left && (rightWidth + (escapementArray[right - 1] * size))
1484 			< mid) {
1485 		rightWidth += (escapementArray[--right] * size);
1486 	}
1487 
1488 	if (left >= right)
1489 		return false;
1490 
1491 	float stringWidth = leftWidth + rightWidth;
1492 	for (uint32 i = left; i < right; ++i)
1493 		stringWidth += (escapementArray[i] * size);
1494 
1495 	if (stringWidth <= width)
1496 		return false;
1497 
1498 	// if there is no space for the ellipsis
1499 	if (width < ellipsisWidth) {
1500 		strcpy(dest, "");
1501 		return true;
1502 	}
1503 
1504 	// The ellipsis now definitely fits, but let's
1505 	// see if we can add another character
1506 	float gap = width - (leftWidth + ellipsisWidth + rightWidth);
1507 	if (left > numChars - right) {
1508 		// try right letter first
1509 		if (optional_char_fits(escapementArray[right - 1], size, gap))
1510 			right--;
1511 		else if (optional_char_fits(escapementArray[left], size, gap))
1512 			left++;
1513 	} else {
1514 		// try left letter first
1515 		if (optional_char_fits(escapementArray[left], size, gap))
1516 			left++;
1517 		else if (optional_char_fits(escapementArray[right - 1], size, gap))
1518 			right--;
1519 	}
1520 
1521 	// copy characters
1522 
1523 	for (uint32 i = 0; i < left; i++) {
1524 		// copy one glyph
1525 		do {
1526 			*dest++ = *source++;
1527 		} while (IsInsideGlyph(*source));
1528 	}
1529 
1530 	dest = write_ellipsis(dest);
1531 
1532 	for (uint32 i = left; i < numChars; i++) {
1533 		// copy one glyph
1534 		do {
1535 			if (i >= right)
1536 				*dest++ = *source++;
1537 			else
1538 				source++;
1539 		} while (IsInsideGlyph(*source));
1540 	}
1541 
1542 	// terminate
1543 	dest[0] = '\0';
1544 	return true;
1545 }
1546 
1547 
1548 // TODO: put into BPrivate namespace
1549 void
1550 truncate_string(const char* string, uint32 mode, float width,
1551 	char* result, const float* escapementArray, float fontSize,
1552 	float ellipsisWidth, int32 length, int32 numChars)
1553 {
1554 	// TODO: that's actually not correct: the string could be smaller than
1555 	// ellipsisWidth
1556 	if (string == NULL /*|| width < ellipsisWidth*/) {
1557 		// we don't have room for a single glyph
1558 		strcpy(result, "");
1559 		return;
1560 	}
1561 
1562 	// iterate over glyphs and copy source into result string
1563 	// one glyph at a time as long as we have room for the "…" yet
1564 	char* dest = result;
1565 	const char* source = string;
1566 	bool truncated = true;
1567 
1568 	switch (mode) {
1569 		case B_TRUNCATE_BEGINNING: {
1570 			dest = copy_from_end(source, dest, numChars, length,
1571 				escapementArray, width, ellipsisWidth, fontSize);
1572 			// "dst" points to the position behind the last glyph that
1573 			// was copied.
1574 			int32 dist = dest - result;
1575 			// we didn't terminate yet
1576 			*dest = 0;
1577 			if (dist < length) {
1578 				// TODO: Is there a smarter way?
1579 				char* temp = new char[dist + 4];
1580 				char* t = temp;
1581 				// append "…"
1582 				t = write_ellipsis(t);
1583 				// shuffle arround strings so that "…" is prepended
1584 				strcpy(t, result);
1585 				strcpy(result, temp);
1586 				delete[] temp;
1587 /*						char t = result[3];
1588 				memmove(&result[3], result, dist + 1);
1589 				write_ellipsis(result);
1590 				result[3] = t;*/
1591 			}
1592 			break;
1593 		}
1594 
1595 		case B_TRUNCATE_END:
1596 			truncated = truncate_end(source, dest, numChars, escapementArray,
1597 				width, ellipsisWidth, fontSize);
1598 			break;
1599 
1600 		case B_TRUNCATE_SMART:
1601 			// TODO: implement, though it was never implemented on R5
1602 			// FALL THROUGH (at least do something)
1603 		case B_TRUNCATE_MIDDLE:
1604 		default:
1605 			truncated = truncate_middle(source, dest, numChars,
1606 				escapementArray, width, ellipsisWidth, fontSize);
1607 			break;
1608 	}
1609 
1610 	if (!truncated) {
1611 		// copy string to destination verbatim
1612 		strlcpy(dest, source, length + 1);
1613 	}
1614 }
1615