1/*
2 * Copyright 2019, Haiku, Inc.
3 * Distributed under the terms of the MIT License.
4 *
5 * Author:
6 *		Preetpal Kaur <preetpalok123@gmail.com>
7 */
8
9
10#include "MouseSettings.h"
11
12#include <File.h>
13#include <FindDirectory.h>
14#include <Path.h>
15#include <String.h>
16#include <View.h>
17
18#include <stdio.h>
19
20
21// The R5 settings file differs from that of Haiku;
22// the latter maps 16 different mouse buttons
23#define R5_COMPATIBLE 0
24
25static const bigtime_t kDefaultClickSpeed = 500000;
26static const int32 kDefaultMouseSpeed = 65536;
27static const int32 kDefaultMouseType = 3; // 3 button mouse
28static const int32 kDefaultAccelerationFactor = 65536;
29static const bool kDefaultAcceptFirstClick = true;
30
31
32MouseSettings::MouseSettings(BString name)
33	:
34	fName(name)
35{
36	if (_RetrieveSettings() != B_OK)
37		Defaults();
38
39	fOriginalSettings = fSettings;
40	fOriginalMode = fMode;
41	fOriginalFocusFollowsMouseMode = fFocusFollowsMouseMode;
42	fOriginalAcceptFirstClick = fAcceptFirstClick;
43}
44
45
46MouseSettings::MouseSettings(mouse_settings settings, BString name)
47	:
48	fSettings(settings)
49{
50	fName = name;
51
52#ifdef DEBUG
53	Dump();
54#endif
55
56	// These are not stored in mouse_settings, get the current values from
57	// app_server
58	// FIXME these should be moved out of the MouseSettings class, since they
59	// are not specific to each mouse, but are global settings.
60	fMode = mouse_mode();
61	fFocusFollowsMouseMode = focus_follows_mouse_mode();
62	fAcceptFirstClick = accept_first_click();
63
64	fOriginalSettings = fSettings;
65	fOriginalMode = fMode;
66	fOriginalFocusFollowsMouseMode = fFocusFollowsMouseMode;
67	fOriginalAcceptFirstClick = fAcceptFirstClick;
68}
69
70
71MouseSettings::~MouseSettings()
72{
73}
74
75
76status_t
77MouseSettings::_GetSettingsPath(BPath& path)
78{
79	status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
80	if (status < B_OK)
81		return status;
82
83	path.Append(mouse_settings_file);
84	return B_OK;
85}
86
87
88status_t
89MouseSettings::_RetrieveSettings()
90{
91	// retrieve current values
92	if (get_mouse_map(&fSettings.map) != B_OK)
93		return B_ERROR;
94	if (get_click_speed(&fSettings.click_speed) != B_OK)
95		return B_ERROR;
96	if (get_mouse_speed(fName, &fSettings.accel.speed) != B_OK)
97		return B_ERROR;
98	if (get_mouse_acceleration(fName, &fSettings.accel.accel_factor) != B_OK)
99		return B_ERROR;
100	if (get_mouse_type(fName, &fSettings.type) != B_OK)
101		return B_ERROR;
102
103	fMode = mouse_mode();
104	fFocusFollowsMouseMode = focus_follows_mouse_mode();
105	fAcceptFirstClick = accept_first_click();
106
107	return B_OK;
108}
109
110
111status_t
112MouseSettings::_LoadLegacySettings()
113{
114	BPath path;
115	if (_GetSettingsPath(path) < B_OK)
116		return B_ERROR;
117
118	BFile file(path.Path(), B_READ_ONLY);
119	if (file.InitCheck() < B_OK)
120		return B_ERROR;
121
122	// Read the settings from the file
123	file.Read((void*)&fSettings, sizeof(mouse_settings));
124
125#ifdef DEBUG
126	Dump();
127#endif
128
129	return B_OK;
130}
131
132
133#ifdef DEBUG
134void
135MouseSettings::Dump()
136{
137	printf("type:\t\t%" B_PRId32 " button mouse\n", fSettings.type);
138	for (int i = 0; i < 5; i++)
139		printf("button[%d]: %" B_PRId32 "\n", i, fSettings.map.button[i]);
140	printf("click speed:\t%" B_PRId64 "\n", fSettings.click_speed);
141	printf("accel:\t\t%s\n", fSettings.accel.enabled ? "enabled" : "disabled");
142	printf("accel factor:\t%" B_PRId32 "\n", fSettings.accel.accel_factor);
143	printf("speed:\t\t%" B_PRId32 "\n", fSettings.accel.speed);
144
145	const char* mode = "unknown";
146	switch (fMode) {
147		case B_NORMAL_MOUSE:
148			mode = "click to focus and raise";
149			break;
150		case B_CLICK_TO_FOCUS_MOUSE:
151			mode = "click to focus";
152			break;
153		case B_FOCUS_FOLLOWS_MOUSE:
154			mode = "focus follows mouse";
155			break;
156	}
157	printf("mouse mode:\t%s\n", mode);
158
159	const char* focus_follows_mouse_mode = "unknown";
160	switch (fFocusFollowsMouseMode) {
161		case B_NORMAL_FOCUS_FOLLOWS_MOUSE:
162			focus_follows_mouse_mode = "normal";
163			break;
164		case B_WARP_FOCUS_FOLLOWS_MOUSE:
165			focus_follows_mouse_mode = "warp";
166			break;
167		case B_INSTANT_WARP_FOCUS_FOLLOWS_MOUSE:
168			focus_follows_mouse_mode = "instant warp";
169			break;
170	}
171	printf("focus follows mouse mode:\t%s\n", focus_follows_mouse_mode);
172	printf("accept first click:\t%s\n",
173		fAcceptFirstClick ? "enabled" : "disabled");
174}
175#endif
176
177
178// Resets the settings to the system defaults
179void
180MouseSettings::Defaults()
181{
182	SetClickSpeed(kDefaultClickSpeed);
183	SetMouseSpeed(kDefaultMouseSpeed);
184	SetMouseType(kDefaultMouseType);
185	SetAccelerationFactor(kDefaultAccelerationFactor);
186	SetMouseMode(B_NORMAL_MOUSE);
187	SetFocusFollowsMouseMode(B_NORMAL_FOCUS_FOLLOWS_MOUSE);
188	SetAcceptFirstClick(kDefaultAcceptFirstClick);
189
190	mouse_map map;
191	if (get_mouse_map(&map) != B_OK) {
192		// Set some default values
193		map.button[0] = B_PRIMARY_MOUSE_BUTTON;
194		map.button[1] = B_SECONDARY_MOUSE_BUTTON;
195		map.button[2] = B_TERTIARY_MOUSE_BUTTON;
196		map.button[3] = B_MOUSE_BUTTON(4);
197		map.button[4] = B_MOUSE_BUTTON(5);
198		map.button[5] = B_MOUSE_BUTTON(6);
199	}
200	SetMapping(map);
201}
202
203
204// Checks if the settings are different then the system defaults
205bool
206MouseSettings::IsDefaultable()
207{
208	return fSettings.click_speed != kDefaultClickSpeed
209		|| fSettings.accel.speed != kDefaultMouseSpeed
210		|| fSettings.type != kDefaultMouseType
211		|| fSettings.accel.accel_factor != kDefaultAccelerationFactor
212		|| fMode != B_NORMAL_MOUSE
213		|| fFocusFollowsMouseMode != B_NORMAL_FOCUS_FOLLOWS_MOUSE
214		|| fAcceptFirstClick != kDefaultAcceptFirstClick
215		|| fSettings.map.button[0] != B_PRIMARY_MOUSE_BUTTON
216		|| fSettings.map.button[1] != B_SECONDARY_MOUSE_BUTTON
217		|| fSettings.map.button[2] != B_TERTIARY_MOUSE_BUTTON
218		|| fSettings.map.button[3] != B_MOUSE_BUTTON(4)
219		|| fSettings.map.button[4] != B_MOUSE_BUTTON(5)
220		|| fSettings.map.button[5] != B_MOUSE_BUTTON(6);
221}
222
223
224//	Reverts to the active settings at program startup
225void
226MouseSettings::Revert()
227{
228	SetClickSpeed(fOriginalSettings.click_speed);
229	SetMouseSpeed(fOriginalSettings.accel.speed);
230	SetMouseType(fOriginalSettings.type);
231	SetAccelerationFactor(fOriginalSettings.accel.accel_factor);
232	SetMouseMode(fOriginalMode);
233	SetFocusFollowsMouseMode(fOriginalFocusFollowsMouseMode);
234	SetAcceptFirstClick(fOriginalAcceptFirstClick);
235
236	SetMapping(fOriginalSettings.map);
237}
238
239
240// Checks if the settings are different then the original settings
241bool
242MouseSettings::IsRevertable()
243{
244	return fSettings.click_speed != fOriginalSettings.click_speed
245		|| fSettings.accel.speed != fOriginalSettings.accel.speed
246		|| fSettings.type != fOriginalSettings.type
247		|| fSettings.accel.accel_factor != fOriginalSettings.accel.accel_factor
248		|| fMode != fOriginalMode
249		|| fFocusFollowsMouseMode != fOriginalFocusFollowsMouseMode
250		|| fAcceptFirstClick != fOriginalAcceptFirstClick
251		|| fSettings.map.button[0] != fOriginalSettings.map.button[0]
252		|| fSettings.map.button[1] != fOriginalSettings.map.button[1]
253		|| fSettings.map.button[2] != fOriginalSettings.map.button[2]
254		|| fSettings.map.button[3] != fOriginalSettings.map.button[3]
255		|| fSettings.map.button[4] != fOriginalSettings.map.button[4]
256		|| fSettings.map.button[5] != fOriginalSettings.map.button[5];
257}
258
259
260void
261MouseSettings::SetMouseType(int32 type)
262{
263	if (set_mouse_type(fName, type) == B_OK)
264		fSettings.type = type;
265}
266
267
268bigtime_t
269MouseSettings::ClickSpeed() const
270{
271	return 1000000LL - fSettings.click_speed;
272		// to correct the Sliders 0-100000 scale
273}
274
275
276void
277MouseSettings::SetClickSpeed(bigtime_t clickSpeed)
278{
279	clickSpeed = 1000000LL - clickSpeed;
280
281	if (set_click_speed(clickSpeed) == B_OK)
282		fSettings.click_speed = clickSpeed;
283}
284
285
286void
287MouseSettings::SetMouseSpeed(int32 speed)
288{
289	if (set_mouse_speed(fName, speed) == B_OK)
290		fSettings.accel.speed = speed;
291}
292
293
294void
295MouseSettings::SetAccelerationFactor(int32 factor)
296{
297	if (set_mouse_acceleration(fName, factor) == B_OK)
298		fSettings.accel.accel_factor = factor;
299}
300
301
302uint32
303MouseSettings::Mapping(int32 index) const
304{
305	return fSettings.map.button[index];
306}
307
308
309void
310MouseSettings::Mapping(mouse_map& map) const
311{
312	map = fSettings.map;
313}
314
315
316void
317MouseSettings::SetMapping(int32 index, uint32 button)
318{
319	fSettings.map.button[index] = button;
320	set_mouse_map(&fSettings.map);
321}
322
323
324void
325MouseSettings::SetMapping(mouse_map& map)
326{
327	if (set_mouse_map(&map) == B_OK)
328		fSettings.map = map;
329}
330
331
332void
333MouseSettings::SetMouseMode(mode_mouse mode)
334{
335	set_mouse_mode(mode);
336	fMode = mode;
337}
338
339
340void
341MouseSettings::SetFocusFollowsMouseMode(mode_focus_follows_mouse mode)
342{
343	set_focus_follows_mouse_mode(mode);
344	fFocusFollowsMouseMode = mode;
345}
346
347
348void
349MouseSettings::SetAcceptFirstClick(bool accept_first_click)
350{
351	set_accept_first_click(accept_first_click);
352	fAcceptFirstClick = accept_first_click;
353}
354
355
356mouse_settings*
357MouseSettings::GetSettings()
358{
359	return &fSettings;
360}
361
362
363MultipleMouseSettings::MultipleMouseSettings()
364{
365	fDeprecatedMouseSettings = NULL;
366	RetrieveSettings();
367
368#ifdef DEBUG
369	Dump();
370#endif
371}
372
373
374MultipleMouseSettings::~MultipleMouseSettings()
375{
376	SaveSettings();
377
378#ifdef DEBUG
379	Dump();
380#endif
381
382	std::map<BString, MouseSettings*>::iterator itr;
383	for (itr = fMouseSettingsObject.begin(); itr != fMouseSettingsObject.end();
384		++itr)
385		delete itr->second;
386
387	delete fDeprecatedMouseSettings;
388}
389
390
391status_t
392MultipleMouseSettings::GetSettingsPath(BPath& path)
393{
394	status_t status = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
395	if (status < B_OK)
396		return status;
397
398	path.Append(mouse_settings_file);
399	return B_OK;
400}
401
402
403void
404MultipleMouseSettings::RetrieveSettings()
405{
406	// retrieve current values
407	// also try to load the window position from disk
408
409	BPath path;
410	if (GetSettingsPath(path) < B_OK)
411		return;
412
413	BFile file(path.Path(), B_READ_ONLY);
414	if (file.InitCheck() < B_OK)
415		return;
416
417	BMessage message;
418
419	if (message.Unflatten(&file) == B_OK) {
420		int i = 0;
421		BString deviceName;
422		mouse_settings* settings;
423		ssize_t size = 0;
424
425		while (message.FindString("mouseDevice", i, &deviceName) == B_OK) {
426			message.FindData(
427				"mouseSettings", B_ANY_TYPE, i, (const void**)&settings, &size);
428			MouseSettings* mouseSettings
429				= new MouseSettings(*settings, deviceName);
430			fMouseSettingsObject.insert(
431				std::pair<BString, MouseSettings*>(deviceName, mouseSettings));
432			i++;
433		}
434	} else {
435		// Does not look like a BMessage, try loading using the old format
436		fDeprecatedMouseSettings = new MouseSettings("");
437		if (fDeprecatedMouseSettings->_LoadLegacySettings() != B_OK) {
438			delete fDeprecatedMouseSettings;
439			fDeprecatedMouseSettings = NULL;
440		}
441	}
442}
443
444
445status_t
446MultipleMouseSettings::Archive(BMessage* into, bool deep) const
447{
448	std::map<BString, MouseSettings*>::const_iterator itr;
449	for (itr = fMouseSettingsObject.begin(); itr != fMouseSettingsObject.end();
450		++itr) {
451		into->AddString("mouseDevice", itr->first);
452		into->AddData("mouseSettings", B_ANY_TYPE, itr->second->GetSettings(),
453			sizeof(*(itr->second->GetSettings())));
454	}
455
456	return B_OK;
457}
458
459
460status_t
461MultipleMouseSettings::SaveSettings()
462{
463	BPath path;
464	status_t status = GetSettingsPath(path);
465	if (status < B_OK)
466		return status;
467
468	BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
469	status = file.InitCheck();
470	if (status != B_OK)
471		return status;
472
473	BMessage message;
474	Archive(&message, true);
475	message.Flatten(&file);
476
477	return B_OK;
478}
479
480
481void
482MultipleMouseSettings::Defaults()
483{
484	std::map<BString, MouseSettings*>::iterator itr;
485	for (itr = fMouseSettingsObject.begin(); itr != fMouseSettingsObject.end();
486		++itr) {
487		itr->second->Defaults();
488	}
489
490}
491
492
493#ifdef DEBUG
494void
495MultipleMouseSettings::Dump()
496{
497	std::map<BString, MouseSettings*>::iterator itr;
498	for (itr = fMouseSettingsObject.begin(); itr != fMouseSettingsObject.end();
499		++itr) {
500		printf("mouse_name:\t%s\n", itr->first.String());
501		itr->second->Dump();
502		printf("\n");
503	}
504}
505#endif
506
507
508MouseSettings*
509MultipleMouseSettings::AddMouseSettings(BString mouse_name)
510{
511	if (fDeprecatedMouseSettings != NULL) {
512		MouseSettings* RetrievedSettings = new(std::nothrow) MouseSettings(
513			*(fDeprecatedMouseSettings->GetSettings()), mouse_name);
514
515		if (RetrievedSettings != NULL) {
516			fMouseSettingsObject.insert(std::pair<BString, MouseSettings*>(
517				mouse_name, RetrievedSettings));
518
519			return RetrievedSettings;
520		}
521	}
522
523	MouseSettings* settings = GetMouseSettings(mouse_name);
524	if (settings)
525		return settings;
526
527	settings = new(std::nothrow) MouseSettings(mouse_name);
528	if (settings == NULL)
529		return NULL;
530
531	fMouseSettingsObject.insert(
532		std::pair<BString, MouseSettings*>(mouse_name, settings));
533	return settings;
534}
535
536
537MouseSettings*
538MultipleMouseSettings::GetMouseSettings(BString mouse_name)
539{
540	std::map<BString, MouseSettings*>::iterator itr;
541	itr = fMouseSettingsObject.find(mouse_name);
542
543	if (itr != fMouseSettingsObject.end())
544		return itr->second;
545	return NULL;
546}
547