1/*
2 * Copyright 2002-2005 Haiku
3 * Distributed under the terms of the MIT license.
4 *
5 * Updated by Sikosis (beos@gravity24hr.com)
6 *
7 * Copyright 1999, Be Incorporated. All Rights Reserved.
8 * This file may be used under the terms of the Be Sample Code License.
9 *
10 * Written by:	Daniel Switkin
11 */
12
13
14#include "PulseApp.h"
15
16#include <stdlib.h>
17#include <stdio.h>
18#include <string.h>
19#include <getopt.h>
20
21#include <Alert.h>
22#include <Catalog.h>
23#include <Deskbar.h>
24#include <Rect.h>
25#include <TextView.h>
26
27#include <syscalls.h>
28
29#include "Common.h"
30#include "PulseWindow.h"
31#include "DeskbarPulseView.h"
32
33#undef B_TRANSLATION_CONTEXT
34#define B_TRANSLATION_CONTEXT "PulseApp"
35
36
37PulseApp::PulseApp(int argc, char **argv)
38	: BApplication(APP_SIGNATURE),
39	fPrefs(new Prefs()),
40	fRunFromReplicant(false),
41	fIsRunning(false),
42	fPrefsWindow(NULL)
43{
44	int mini = false, deskbar = false, normal = false;
45	uint32 framecolor = 0, activecolor = 0, idlecolor = 0;
46
47	while (1) {
48		int option_index = 0;
49		static struct option long_options[] = {
50			{"deskbar", 0, &deskbar, true},
51			{"width", 1, 0, 'w'},
52			{"framecolor", 1, 0, 0},
53			{"activecolor", 1, 0, 0},
54			{"idlecolor", 1, 0, 0},
55			{"mini", 0, &mini, true},
56			{"normal", 0, &normal, true},
57			{"help", 0, 0, 'h'},
58			{0,0,0,0}
59		};
60		int c = getopt_long(argc, argv, "hw:", long_options, &option_index);
61		if (c == -1)
62			break;
63
64		switch (c) {
65			case 0:
66				switch (option_index) {
67					case 2: /* framecolor */
68					case 3: /* activecolor */
69					case 4: /* idlecolor */
70						uint32 rgb = strtoul(optarg, NULL, 0);
71						rgb = rgb << 8;
72						rgb |= 0x000000ff;
73
74						switch (option_index) {
75							case 2:
76								framecolor = rgb;
77								break;
78							case 3:
79								activecolor = rgb;
80								break;
81							case 4:
82								idlecolor = rgb;
83								break;
84						}
85						break;
86				}
87				break;
88			case 'w':
89				fPrefs->deskbar_icon_width = atoi(optarg);
90				if (fPrefs->deskbar_icon_width < GetMinimumViewWidth())
91					fPrefs->deskbar_icon_width = GetMinimumViewWidth();
92				else if (fPrefs->deskbar_icon_width > 50) fPrefs->deskbar_icon_width = 50;
93				break;
94			case 'h':
95			case '?':
96				Usage();
97				break;
98			default:
99				printf("?? getopt returned character code 0%o ??\n", c);
100				break;
101		}
102	}
103
104	if (deskbar) {
105		fPrefs->window_mode = DESKBAR_MODE;
106		if (activecolor != 0)
107			fPrefs->deskbar_active_color = activecolor;
108		if (idlecolor != 0)
109			fPrefs->deskbar_idle_color = idlecolor;
110		if (framecolor != 0)
111			fPrefs->deskbar_frame_color = framecolor;
112	} else if (mini) {
113		fPrefs->window_mode = MINI_WINDOW_MODE;
114		if (activecolor != 0)
115			fPrefs->mini_active_color = activecolor;
116		if (idlecolor != 0)
117			fPrefs->mini_idle_color = idlecolor;
118		if (framecolor != 0)
119			fPrefs->mini_frame_color = framecolor;
120	} else if (normal)
121		fPrefs->window_mode = NORMAL_WINDOW_MODE;
122
123	fPrefs->Save();
124}
125
126
127void
128PulseApp::ReadyToRun()
129{
130	if (!fRunFromReplicant)
131		BuildPulse();
132
133	fIsRunning = true;
134}
135
136
137void
138PulseApp::BuildPulse()
139{
140	PulseWindow *pulseWindow = NULL;
141
142	if (fPrefs->window_mode == MINI_WINDOW_MODE)
143		pulseWindow = new PulseWindow(fPrefs->mini_window_rect);
144	else
145		pulseWindow = new PulseWindow(fPrefs->normal_window_rect);
146
147	pulseWindow->MoveOnScreen(B_MOVE_IF_PARTIALLY_OFFSCREEN);
148	pulseWindow->Show();
149}
150
151
152PulseApp::~PulseApp()
153{
154	fPrefs->Save();
155
156	delete fPrefs;
157}
158
159
160void
161PulseApp::MessageReceived(BMessage* message)
162{
163	switch (message->what) {
164		case PV_PREFERENCES:
165		{
166			// This message can be posted before ReadyToRun from
167			// BRoster::Launch, in that case, take note to not show the main
168			// window but only the preferences
169			if (!fIsRunning)
170				fRunFromReplicant = true;
171			BMessenger from;
172			message->FindMessenger("settingsListener", &from);
173
174
175			if (fPrefsWindow != NULL) {
176				fPrefsWindow->Activate(true);
177				break;
178			}
179			// If the window is already open, bring it to the front
180			if (fPrefsWindow != NULL) {
181				fPrefsWindow->Activate(true);
182				break;
183			}
184			// Otherwise launch a new preferences window
185			PulseApp *pulseapp = (PulseApp *)be_app;
186			fPrefsWindow = new PrefsWindow(pulseapp->fPrefs->prefs_window_rect,
187				B_TRANSLATE("Pulse settings"), &from,
188				pulseapp->fPrefs);
189			if (fRunFromReplicant) {
190				fPrefsWindow->SetFlags(fPrefsWindow->Flags()
191					| B_QUIT_ON_WINDOW_CLOSE);
192			}
193			fPrefsWindow->Show();
194
195			break;
196		}
197
198		case PV_ABOUT:
199			// This message can be posted before ReadyToRun from
200			// BRoster::Launch, in that case, take note to not show the main
201			// window but only the about box
202			if (!fIsRunning)
203				fRunFromReplicant = true;
204			PostMessage(B_ABOUT_REQUESTED);
205			break;
206
207		case PRV_QUIT:
208			fPrefsWindow = NULL;
209			fRunFromReplicant = false;
210			break;
211
212		default:
213			BApplication::MessageReceived(message);
214			break;
215	}
216}
217
218
219void
220PulseApp::AboutRequested()
221{
222	BString name = B_TRANSLATE("Pulse");
223
224	BString message = B_TRANSLATE(
225		"%s\n\nBy David Ramsey and Arve Hj��nnev��g\n"
226		"Revised by Daniel Switkin\n");
227	message.ReplaceFirst("%s", name);
228	BAlert *alert = new BAlert(B_TRANSLATE("Info"),
229		message.String(), B_TRANSLATE("OK"));
230
231	if (fRunFromReplicant)
232		alert->SetFlags(alert->Flags() | B_QUIT_ON_WINDOW_CLOSE);
233
234	BTextView* view = alert->TextView();
235	BFont font;
236
237	view->SetStylable(true);
238	view->GetFont(&font);
239
240	font.SetSize(18);
241	font.SetFace(B_BOLD_FACE);
242	view->SetFontAndColor(0, name.Length(), &font);
243	alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
244	// Use the asynchronous version so we don't block the window's thread
245	alert->Go(NULL);
246	fRunFromReplicant = false;
247}
248
249//	#pragma mark -
250
251
252/** Make sure we don't disable the last CPU - this is needed by
253 *	descendants of PulseView for the popup menu and for CPUButton
254 *	both as a replicant and not.
255 */
256
257bool
258LastEnabledCPU(unsigned int my_cpu)
259{
260	system_info sys_info;
261	get_system_info(&sys_info);
262	if (sys_info.cpu_count == 1)
263		return true;
264
265	for (unsigned int x = 0; x < sys_info.cpu_count; x++) {
266		if (x == my_cpu)
267			continue;
268		if (_kern_cpu_enabled(x) == 1)
269			return false;
270	}
271	return true;
272}
273
274
275/**	Ensure that the mini mode and deskbar mode always show an indicator
276 *	for each CPU, at least one pixel wide.
277 */
278
279int
280GetMinimumViewWidth()
281{
282	system_info sys_info;
283	get_system_info(&sys_info);
284	return (sys_info.cpu_count * 2) + 1;
285}
286
287
288void
289Usage()
290{
291	puts(B_TRANSLATE("Usage: Pulse [--mini] [-w width] [--width=width]\n"
292		"\t[--deskbar] [--normal] [--framecolor 0xrrggbb]\n"
293		"\t[--activecolor 0xrrggbb] [--idlecolor 0xrrggbb]"));
294	exit(0);
295}
296
297
298bool
299LoadInDeskbar()
300{
301	PulseApp *pulseapp = (PulseApp *)be_app;
302	BDeskbar *deskbar = new BDeskbar();
303	// Don't allow two copies in the Deskbar at once
304	if (deskbar->HasItem("DeskbarPulseView")) {
305		delete deskbar;
306		return false;
307	}
308
309	// Must be 16 pixels high, the width is retrieved from the Prefs class
310	int width = pulseapp->fPrefs->deskbar_icon_width;
311	int min_width = GetMinimumViewWidth();
312	if (width < min_width) {
313		pulseapp->fPrefs->deskbar_icon_width = min_width;
314		width = min_width;
315	}
316
317	float height = deskbar->MaxItemHeight();
318	BRect rect(0, 0, width - 1, height - 1);
319	DeskbarPulseView *replicant = new DeskbarPulseView(rect);
320	status_t err = deskbar->AddItem(replicant);
321	delete replicant;
322	delete deskbar;
323	if (err != B_OK) {
324		BString message;
325		snprintf(message.LockBuffer(512), 512,
326			B_TRANSLATE("Installing in Deskbar failed\n%s"), strerror(err));
327		message.UnlockBuffer();
328		BAlert *alert = new BAlert(B_TRANSLATE("Error"),
329			message.String(), B_TRANSLATE("OK"));
330		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
331		alert->Go(NULL);
332		return false;
333	}
334
335	return true;
336}
337
338
339int
340main(int argc, char **argv)
341{
342	PulseApp *pulseapp = new PulseApp(argc, argv);
343	pulseapp->Run();
344	delete pulseapp;
345	return 0;
346}
347