1/*
2 * Copyright 2001-2019, Haiku, Inc.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Axel D��rfler, axeld@pinc-software.de
7 *		Ingo Weinhold, bonefish@@users.sf.net
8 *		Jacob Secunda
9 */
10
11
12#include <AppMisc.h>
13
14#include <stdlib.h>
15#include <string.h>
16#include <sys/utsname.h>
17#include <unistd.h>
18
19#include <Entry.h>
20#include <image.h>
21#include <Messenger.h>
22#include <OS.h>
23#include <Window.h>
24
25#include <AutoDeleter.h>
26#include <ServerLink.h>
27#include <ServerProtocol.h>
28#include <WindowInfo.h>
29
30
31namespace BPrivate {
32
33
34static team_id sCurrentTeam = -1;
35
36
37/*!	\brief Returns the path to an application's executable.
38	\param team The application's team ID.
39	\param buffer A pointer to a pre-allocated character array of at least
40		   size B_PATH_NAME_LENGTH to be filled in by this function.
41	\return
42	- \c B_OK: Everything went fine.
43	- \c B_BAD_VALUE: \c NULL \a buffer.
44	- another error code
45*/
46status_t
47get_app_path(team_id team, char *buffer)
48{
49	// The only way to get the path to the application's executable seems to
50	// be to get an image_info of its image, which also contains a path.
51	// Several images may belong to the team (libraries, add-ons), but only
52	// the one in question should be typed B_APP_IMAGE.
53	if (!buffer)
54		return B_BAD_VALUE;
55
56	image_info info;
57	int32 cookie = 0;
58
59	while (get_next_image_info(team, &cookie, &info) == B_OK) {
60		if (info.type == B_APP_IMAGE) {
61			strlcpy(buffer, info.name, B_PATH_NAME_LENGTH - 1);
62			return B_OK;
63		}
64	}
65
66	return B_ENTRY_NOT_FOUND;
67}
68
69
70/*!	\brief Returns the path to the application's executable.
71	\param buffer A pointer to a pre-allocated character array of at least
72		   size B_PATH_NAME_LENGTH to be filled in by this function.
73	\return
74	- \c B_OK: Everything went fine.
75	- \c B_BAD_VALUE: \c NULL \a buffer.
76	- another error code
77*/
78status_t
79get_app_path(char *buffer)
80{
81	return get_app_path(B_CURRENT_TEAM, buffer);
82}
83
84
85/*!	\brief Returns an entry_ref referring to an application's executable.
86	\param team The application's team ID.
87	\param ref A pointer to a pre-allocated entry_ref to be initialized
88		   to an entry_ref referring to the application's executable.
89	\param traverse If \c true, the function traverses symbolic links.
90	\return
91	- \c B_OK: Everything went fine.
92	- \c B_BAD_VALUE: \c NULL \a ref.
93	- another error code
94*/
95status_t
96get_app_ref(team_id team, entry_ref *ref, bool traverse)
97{
98	status_t error = (ref ? B_OK : B_BAD_VALUE);
99	char appFilePath[B_PATH_NAME_LENGTH];
100
101	if (error == B_OK)
102		error = get_app_path(team, appFilePath);
103
104	if (error == B_OK) {
105		BEntry entry(appFilePath, traverse);
106		error = entry.GetRef(ref);
107	}
108
109	return error;
110}
111
112
113/*!	\brief Returns an entry_ref referring to the application's executable.
114	\param ref A pointer to a pre-allocated entry_ref to be initialized
115		   to an entry_ref referring to the application's executable.
116	\param traverse If \c true, the function traverses symbolic links.
117	\return
118	- \c B_OK: Everything went fine.
119	- \c B_BAD_VALUE: \c NULL \a ref.
120	- another error code
121*/
122status_t
123get_app_ref(entry_ref *ref, bool traverse)
124{
125	return get_app_ref(B_CURRENT_TEAM, ref, traverse);
126}
127
128
129/*!	\brief Returns the ID of the current team.
130	\return The ID of the current team.
131*/
132team_id
133current_team()
134{
135	if (sCurrentTeam < 0) {
136		thread_info info;
137		if (get_thread_info(find_thread(NULL), &info) == B_OK)
138			sCurrentTeam = info.team;
139	}
140	return sCurrentTeam;
141}
142
143
144void
145init_team_after_fork()
146{
147	sCurrentTeam = -1;
148}
149
150
151/*!	Returns the ID of the supplied team's main thread.
152	\param team The team.
153	\return
154	- The thread ID of the supplied team's main thread
155	- \c B_BAD_TEAM_ID: The supplied team ID does not identify a running team.
156	- another error code
157*/
158thread_id
159main_thread_for(team_id team)
160{
161	// Under Haiku the team ID is equal to it's main thread ID. We just get
162	// a team info to verify the existence of the team.
163	team_info info;
164	status_t error = get_team_info(team, &info);
165	return error == B_OK ? team : error;
166}
167
168
169/*!	\brief Returns whether the application identified by the supplied
170		   \c team_id is currently showing a modal window.
171	\param team the ID of the application in question.
172	\return \c true, if the application is showing a modal window, \c false
173			otherwise.
174*/
175bool
176is_app_showing_modal_window(team_id team)
177{
178	int32 tokenCount;
179	int32* tokens = get_token_list(team, &tokenCount);
180
181	if (tokens != NULL) {
182		MemoryDeleter tokenDeleter(tokens);
183
184		for (int32 index = 0; index < tokenCount; index++) {
185			client_window_info* matchWindowInfo = get_window_info(tokens[index]);
186			if (matchWindowInfo == NULL) {
187				// That window probably closed. Just go to the next one.
188				continue;
189			}
190
191			window_feel theFeel = (window_feel)matchWindowInfo->feel;
192			free(matchWindowInfo);
193
194			if (theFeel == B_MODAL_SUBSET_WINDOW_FEEL
195				|| theFeel == B_MODAL_APP_WINDOW_FEEL
196				|| theFeel == B_MODAL_ALL_WINDOW_FEEL)
197				return true;
198		}
199	}
200
201	return false;
202}
203
204
205#ifndef HAIKU_TARGET_PLATFORM_LIBBE_TEST
206
207
208/*!	Creates a connection with the desktop.
209*/
210status_t
211create_desktop_connection(ServerLink* link, const char* name, int32 capacity)
212{
213	// Create the port so that the app_server knows where to send messages
214	port_id clientPort = create_port(capacity, name);
215	if (clientPort < 0)
216		return clientPort;
217
218	link->SetReceiverPort(clientPort);
219
220	BMessage request(AS_GET_DESKTOP);
221	request.AddInt32("user", getuid());
222	request.AddInt32("version", AS_PROTOCOL_VERSION);
223	request.AddString("target", getenv("TARGET_SCREEN"));
224
225	BMessenger server("application/x-vnd.Haiku-app_server");
226	BMessage reply;
227	status_t status = server.SendMessage(&request, &reply);
228	if (status != B_OK)
229		return status;
230
231	port_id desktopPort = reply.GetInt32("port", B_ERROR);
232	if (desktopPort < 0)
233		return desktopPort;
234
235	link->SetSenderPort(desktopPort);
236	return B_OK;
237}
238
239
240#else // HAIKU_TARGET_PLATFORM_LIBBE_TEST
241
242
243static port_id sServerPort = -1;
244
245
246port_id
247get_app_server_port()
248{
249	if (sServerPort < 0) {
250		// No need for synchronization - in the worst case, we'll call
251		// find_port() twice.
252		sServerPort = find_port(SERVER_PORT_NAME);
253	}
254
255	return sServerPort;
256}
257
258
259/*! Creates a connection with the desktop.
260*/
261status_t
262create_desktop_connection(ServerLink* link, const char* name, int32 capacity)
263{
264	port_id serverPort = get_app_server_port();
265	if (serverPort < 0)
266		return serverPort;
267
268	// Create the port so that the app_server knows where to send messages
269	port_id clientPort = create_port(capacity, name);
270	if (clientPort < 0)
271		return clientPort;
272
273	link->SetTo(serverPort, clientPort);
274
275	link->StartMessage(AS_GET_DESKTOP);
276	link->Attach<port_id>(clientPort);
277	link->Attach<int32>(getuid());
278	link->AttachString(getenv("TARGET_SCREEN"));
279	link->Attach<int32>(AS_PROTOCOL_VERSION);
280
281	int32 code;
282	if (link->FlushWithReply(code) != B_OK || code != B_OK) {
283		link->SetSenderPort(-1);
284		return B_ERROR;
285	}
286
287	link->Read<port_id>(&serverPort);
288	link->SetSenderPort(serverPort);
289
290	return B_OK;
291}
292
293
294#endif // HAIKU_TARGET_PLATFORM_LIBBE_TEST
295
296
297} // namespace BPrivate
298