source: mainline/uspace/lib/graph/graph.c@ 38d150e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 38d150e was 38d150e, checked in by Jiri Svoboda <jiri@…>, 8 years ago

Prefer to get memory allocation functions through the standard stdlib header.

  • Property mode set to 100644
File size: 14.6 KB
RevLine 
[6d5e378]1/*
2 * Copyright (c) 2011 Petr Koupy
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup graph
30 * @{
31 */
32/**
33 * @file
34 */
35
36#include <assert.h>
37#include <errno.h>
38#include <inttypes.h>
39#include <stdio.h>
40#include <as.h>
[38d150e]41#include <stdlib.h>
[6d5e378]42#include "graph.h"
43
44#define NAMESPACE "graphemu"
45#define VISUALIZER_NAME "vsl"
46#define RENDERER_NAME "rnd"
47
48static sysarg_t namespace_idx = 0;
49static sysarg_t visualizer_idx = 0;
50static sysarg_t renderer_idx = 0;
51
52static LIST_INITIALIZE(visualizer_list);
53static LIST_INITIALIZE(renderer_list);
54
55static FIBRIL_MUTEX_INITIALIZE(visualizer_list_mtx);
56static FIBRIL_MUTEX_INITIALIZE(renderer_list_mtx);
57
58visualizer_t *graph_alloc_visualizer(void)
59{
[df2e621c]60 return ((visualizer_t *) malloc(sizeof(visualizer_t)));
[6d5e378]61}
62
63renderer_t *graph_alloc_renderer(void)
64{
65 // TODO
[df2e621c]66 return ((renderer_t *) malloc(sizeof(renderer_t)));
[6d5e378]67}
68
69void graph_init_visualizer(visualizer_t *vs)
70{
71 link_initialize(&vs->link);
72 atomic_set(&vs->ref_cnt, 0);
73 vs->notif_sess = NULL;
74 fibril_mutex_initialize(&vs->mode_mtx);
75 list_initialize(&vs->modes);
76 vs->mode_set = false;
77 vs->cells.data = NULL;
78 vs->dev_ctx = NULL;
79}
80
81void graph_init_renderer(renderer_t *rnd)
82{
83 // TODO
84 link_initialize(&rnd->link);
85 atomic_set(&rnd->ref_cnt, 0);
86}
87
88int graph_register_visualizer(visualizer_t *vs)
89{
90 char node[LOC_NAME_MAXLEN + 1];
91 snprintf(node, LOC_NAME_MAXLEN, "%s%zu/%s%zu", NAMESPACE,
92 namespace_idx, VISUALIZER_NAME, visualizer_idx++);
[df2e621c]93
[6d5e378]94 category_id_t cat;
[df2e621c]95 int rc = loc_category_get_id("visualizer", &cat, 0);
96 if (rc != EOK)
[6d5e378]97 return rc;
[df2e621c]98
[6d5e378]99 rc = loc_service_register(node, &vs->reg_svc_handle);
[df2e621c]100 if (rc != EOK)
[6d5e378]101 return rc;
[df2e621c]102
[6d5e378]103 rc = loc_service_add_to_cat(vs->reg_svc_handle, cat);
104 if (rc != EOK) {
105 loc_service_unregister(vs->reg_svc_handle);
106 return rc;
107 }
[df2e621c]108
[6d5e378]109 fibril_mutex_lock(&visualizer_list_mtx);
110 list_append(&vs->link, &visualizer_list);
111 fibril_mutex_unlock(&visualizer_list_mtx);
[df2e621c]112
[6d5e378]113 return rc;
114}
115
116int graph_register_renderer(renderer_t *rnd)
117{
118 char node[LOC_NAME_MAXLEN + 1];
119 snprintf(node, LOC_NAME_MAXLEN, "%s%zu/%s%zu", NAMESPACE,
120 namespace_idx, RENDERER_NAME, renderer_idx++);
[df2e621c]121
[6d5e378]122 category_id_t cat;
[df2e621c]123 int rc = loc_category_get_id("renderer", &cat, 0);
124 if (rc != EOK)
[6d5e378]125 return rc;
[df2e621c]126
[6d5e378]127 rc = loc_service_register(node, &rnd->reg_svc_handle);
[df2e621c]128 if (rc != EOK)
[6d5e378]129 return rc;
[df2e621c]130
[6d5e378]131 rc = loc_service_add_to_cat(rnd->reg_svc_handle, cat);
132 if (rc != EOK) {
133 loc_service_unregister(rnd->reg_svc_handle);
134 return rc;
135 }
[df2e621c]136
[6d5e378]137 fibril_mutex_lock(&renderer_list_mtx);
138 list_append(&rnd->link, &renderer_list);
139 fibril_mutex_unlock(&renderer_list_mtx);
[df2e621c]140
[6d5e378]141 return rc;
142}
143
144visualizer_t *graph_get_visualizer(sysarg_t handle)
145{
146 visualizer_t *vs = NULL;
[df2e621c]147
[6d5e378]148 fibril_mutex_lock(&visualizer_list_mtx);
[df2e621c]149
[feeac0d]150 list_foreach(visualizer_list, link, visualizer_t, vcur) {
151 if (vcur->reg_svc_handle == handle) {
152 vs = vcur;
[6d5e378]153 break;
154 }
155 }
[df2e621c]156
[6d5e378]157 fibril_mutex_unlock(&visualizer_list_mtx);
[df2e621c]158
[6d5e378]159 return vs;
160}
161
162renderer_t *graph_get_renderer(sysarg_t handle)
163{
164 renderer_t *rnd = NULL;
[df2e621c]165
[6d5e378]166 fibril_mutex_lock(&renderer_list_mtx);
[df2e621c]167
[feeac0d]168 list_foreach(renderer_list, link, renderer_t, rcur) {
169 if (rcur->reg_svc_handle == handle) {
170 rnd = rcur;
[6d5e378]171 break;
172 }
173 }
[df2e621c]174
[6d5e378]175 fibril_mutex_unlock(&renderer_list_mtx);
[df2e621c]176
[6d5e378]177 return rnd;
178}
179
180int graph_unregister_visualizer(visualizer_t *vs)
181{
182 fibril_mutex_lock(&visualizer_list_mtx);
[df2e621c]183 int rc = loc_service_unregister(vs->reg_svc_handle);
[6d5e378]184 list_remove(&vs->link);
185 fibril_mutex_unlock(&visualizer_list_mtx);
[df2e621c]186
[6d5e378]187 return rc;
188}
189
190int graph_unregister_renderer(renderer_t *rnd)
191{
192 fibril_mutex_lock(&renderer_list_mtx);
[df2e621c]193 int rc = loc_service_unregister(rnd->reg_svc_handle);
[6d5e378]194 list_remove(&rnd->link);
195 fibril_mutex_unlock(&renderer_list_mtx);
[df2e621c]196
[6d5e378]197 return rc;
198}
199
200void graph_destroy_visualizer(visualizer_t *vs)
201{
202 assert(atomic_get(&vs->ref_cnt) == 0);
203 assert(vs->notif_sess == NULL);
204 assert(!fibril_mutex_is_locked(&vs->mode_mtx));
205 assert(list_empty(&vs->modes));
206 assert(vs->mode_set == false);
207 assert(vs->cells.data == NULL);
208 assert(vs->dev_ctx == NULL);
[df2e621c]209
[6d5e378]210 free(vs);
211}
212
213void graph_destroy_renderer(renderer_t *rnd)
214{
215 // TODO
216 assert(atomic_get(&rnd->ref_cnt) == 0);
[df2e621c]217
[6d5e378]218 free(rnd);
219}
220
221int graph_notify_mode_change(async_sess_t *sess, sysarg_t handle, sysarg_t mode_idx)
222{
223 async_exch_t *exch = async_exchange_begin(sess);
224 int ret = async_req_2_0(exch, VISUALIZER_MODE_CHANGE, handle, mode_idx);
225 async_exchange_end(exch);
[df2e621c]226
[6d5e378]227 return ret;
228}
229
230int graph_notify_disconnect(async_sess_t *sess, sysarg_t handle)
231{
232 async_exch_t *exch = async_exchange_begin(sess);
233 int ret = async_req_1_0(exch, VISUALIZER_DISCONNECT, handle);
234 async_exchange_end(exch);
[df2e621c]235
[6d5e378]236 async_hangup(sess);
[df2e621c]237
[6d5e378]238 return ret;
239}
240
241static void vs_claim(visualizer_t *vs, ipc_callid_t iid, ipc_call_t *icall)
242{
243 vs->client_side_handle = IPC_GET_ARG1(*icall);
244 int rc = vs->ops.claim(vs);
245 async_answer_0(iid, rc);
246}
247
248static void vs_yield(visualizer_t *vs, ipc_callid_t iid, ipc_call_t *icall)
249{
250 /* Deallocate resources for the current mode. */
251 if (vs->mode_set) {
252 if (vs->cells.data != NULL) {
253 as_area_destroy((void *) vs->cells.data);
254 vs->cells.data = NULL;
255 }
256 }
[df2e621c]257
[6d5e378]258 /* Driver might also deallocate resources for the current mode. */
259 int rc = vs->ops.yield(vs);
[df2e621c]260
[6d5e378]261 /* Now that the driver was given a chance to deallocate resources,
262 * current mode can be unset. */
[df2e621c]263 if (vs->mode_set)
[6d5e378]264 vs->mode_set = false;
[df2e621c]265
[6d5e378]266 async_answer_0(iid, rc);
267}
268
269static void vs_enumerate_modes(visualizer_t *vs, ipc_callid_t iid, ipc_call_t *icall)
270{
[df2e621c]271 ipc_callid_t callid;
272 size_t len;
273
274 if (!async_data_read_receive(&callid, &len)) {
275 async_answer_0(callid, EREFUSED);
276 async_answer_0(iid, EREFUSED);
277 return;
278 }
279
[6d5e378]280 fibril_mutex_lock(&vs->mode_mtx);
281 link_t *link = list_nth(&vs->modes, IPC_GET_ARG1(*icall));
[df2e621c]282
[6d5e378]283 if (link != NULL) {
284 vslmode_list_element_t *mode_elem =
285 list_get_instance(link, vslmode_list_element_t, link);
[df2e621c]286
287 int rc = async_data_read_finalize(callid, &mode_elem->mode, len);
288 async_answer_0(iid, rc);
[6d5e378]289 } else {
[df2e621c]290 async_answer_0(callid, ENOENT);
[6d5e378]291 async_answer_0(iid, ENOENT);
292 }
[df2e621c]293
294 fibril_mutex_unlock(&vs->mode_mtx);
[6d5e378]295}
296
297static void vs_get_default_mode(visualizer_t *vs, ipc_callid_t iid, ipc_call_t *icall)
298{
[df2e621c]299 ipc_callid_t callid;
300 size_t len;
301
302 if (!async_data_read_receive(&callid, &len)) {
303 async_answer_0(callid, EREFUSED);
304 async_answer_0(iid, EREFUSED);
305 return;
306 }
307
[6d5e378]308 fibril_mutex_lock(&vs->mode_mtx);
309 vslmode_list_element_t *mode_elem = NULL;
[df2e621c]310
[feeac0d]311 list_foreach(vs->modes, link, vslmode_list_element_t, cur) {
[6d5e378]312 if (cur->mode.index == vs->def_mode_idx) {
313 mode_elem = cur;
314 break;
315 }
316 }
[df2e621c]317
[6d5e378]318 if (mode_elem != NULL) {
[df2e621c]319 int rc = async_data_read_finalize(callid, &mode_elem->mode, len);
320 async_answer_0(iid, rc);
[6d5e378]321 } else {
322 fibril_mutex_unlock(&vs->mode_mtx);
[df2e621c]323 async_answer_0(callid, ENOENT);
[6d5e378]324 async_answer_0(iid, ENOENT);
325 }
[df2e621c]326
327 fibril_mutex_unlock(&vs->mode_mtx);
[6d5e378]328}
329
330static void vs_get_current_mode(visualizer_t *vs, ipc_callid_t iid, ipc_call_t *icall)
331{
[df2e621c]332 ipc_callid_t callid;
333 size_t len;
334
335 if (!async_data_read_receive(&callid, &len)) {
336 async_answer_0(callid, EREFUSED);
337 async_answer_0(iid, EREFUSED);
338 return;
339 }
340
[6d5e378]341 if (vs->mode_set) {
342 int rc = async_data_read_finalize(callid, &vs->cur_mode, len);
[df2e621c]343 async_answer_0(iid, rc);
[6d5e378]344 } else {
[df2e621c]345 async_answer_0(callid, ENOENT);
[6d5e378]346 async_answer_0(iid, ENOENT);
347 }
348}
349
350static void vs_get_mode(visualizer_t *vs, ipc_callid_t iid, ipc_call_t *icall)
351{
[df2e621c]352 ipc_callid_t callid;
353 size_t len;
354
355 if (!async_data_read_receive(&callid, &len)) {
356 async_answer_0(callid, EREFUSED);
357 async_answer_0(iid, EREFUSED);
358 return;
359 }
360
[6d5e378]361 sysarg_t mode_idx = IPC_GET_ARG1(*icall);
[df2e621c]362
[6d5e378]363 fibril_mutex_lock(&vs->mode_mtx);
364 vslmode_list_element_t *mode_elem = NULL;
[df2e621c]365
[feeac0d]366 list_foreach(vs->modes, link, vslmode_list_element_t, cur) {
[6d5e378]367 if (cur->mode.index == mode_idx) {
368 mode_elem = cur;
369 break;
370 }
371 }
[df2e621c]372
[6d5e378]373 if (mode_elem != NULL) {
[df2e621c]374 int rc = async_data_read_finalize(callid, &mode_elem->mode, len);
375 async_answer_0(iid, rc);
[6d5e378]376 } else {
[df2e621c]377 async_answer_0(callid, ENOENT);
[6d5e378]378 async_answer_0(iid, ENOENT);
379 }
[df2e621c]380
381 fibril_mutex_unlock(&vs->mode_mtx);
[6d5e378]382}
383
384static void vs_set_mode(visualizer_t *vs, ipc_callid_t iid, ipc_call_t *icall)
385{
[df2e621c]386 ipc_callid_t callid;
387 size_t size;
388 unsigned int flags;
389
390 /* Retrieve the shared cell storage for the new mode. */
391 if (!async_share_out_receive(&callid, &size, &flags)) {
392 async_answer_0(callid, EREFUSED);
393 async_answer_0(iid, EREFUSED);
394 return;
395 }
396
[6d5e378]397 /* Retrieve mode index and version. */
398 sysarg_t mode_idx = IPC_GET_ARG1(*icall);
399 sysarg_t mode_version = IPC_GET_ARG2(*icall);
[df2e621c]400
[6d5e378]401 /* Find mode in the list. */
402 fibril_mutex_lock(&vs->mode_mtx);
403 vslmode_list_element_t *mode_elem = NULL;
[df2e621c]404
[feeac0d]405 list_foreach(vs->modes, link, vslmode_list_element_t, cur) {
[6d5e378]406 if (cur->mode.index == mode_idx) {
407 mode_elem = cur;
408 break;
409 }
410 }
[df2e621c]411
412 if (mode_elem == NULL) {
[6d5e378]413 fibril_mutex_unlock(&vs->mode_mtx);
[df2e621c]414 async_answer_0(callid, ENOENT);
[6d5e378]415 async_answer_0(iid, ENOENT);
416 return;
417 }
[df2e621c]418
419 /* Extract mode description from the list node. */
420 vslmode_t new_mode = mode_elem->mode;
421 fibril_mutex_unlock(&vs->mode_mtx);
422
[6d5e378]423 /* Check whether the mode is still up-to-date. */
424 if (new_mode.version != mode_version) {
[df2e621c]425 async_answer_0(callid, EINVAL);
[6d5e378]426 async_answer_0(iid, EINVAL);
427 return;
428 }
[df2e621c]429
[6d5e378]430 void *new_cell_storage;
[df2e621c]431 int rc = async_share_out_finalize(callid, &new_cell_storage);
[6d5e378]432 if ((rc != EOK) || (new_cell_storage == AS_MAP_FAILED)) {
433 async_answer_0(iid, ENOMEM);
434 return;
435 }
[df2e621c]436
[6d5e378]437 /* Change device internal state. */
438 rc = vs->ops.change_mode(vs, new_mode);
[df2e621c]439
[6d5e378]440 /* Device driver could not establish new mode. Rollback. */
441 if (rc != EOK) {
442 as_area_destroy(new_cell_storage);
443 async_answer_0(iid, ENOMEM);
444 return;
445 }
[df2e621c]446
447 /*
448 * Because resources for the new mode were successfully
449 * claimed, it is finally possible to free resources
450 * allocated for the old mode.
451 */
[6d5e378]452 if (vs->mode_set) {
453 if (vs->cells.data != NULL) {
454 as_area_destroy((void *) vs->cells.data);
455 vs->cells.data = NULL;
456 }
457 }
[df2e621c]458
[6d5e378]459 /* Insert new mode into the visualizer. */
460 vs->cells.width = new_mode.screen_width;
461 vs->cells.height = new_mode.screen_height;
462 vs->cells.data = (pixel_t *) new_cell_storage;
463 vs->cur_mode = new_mode;
464 vs->mode_set = true;
[df2e621c]465
[6d5e378]466 async_answer_0(iid, EOK);
467}
468
469static void vs_update_damaged_region(visualizer_t *vs, ipc_callid_t iid, ipc_call_t *icall)
470{
471 sysarg_t x_offset = (IPC_GET_ARG5(*icall) >> 16);
472 sysarg_t y_offset = (IPC_GET_ARG5(*icall) & 0x0000ffff);
[df2e621c]473
[6d5e378]474 int rc = vs->ops.handle_damage(vs,
475 IPC_GET_ARG1(*icall), IPC_GET_ARG2(*icall),
476 IPC_GET_ARG3(*icall), IPC_GET_ARG4(*icall),
[df2e621c]477 x_offset, y_offset);
[6d5e378]478 async_answer_0(iid, rc);
479}
480
481static void vs_suspend(visualizer_t *vs, ipc_callid_t iid, ipc_call_t *icall)
482{
483 int rc = vs->ops.suspend(vs);
484 async_answer_0(iid, rc);
485}
486
487static void vs_wakeup(visualizer_t *vs, ipc_callid_t iid, ipc_call_t *icall)
488{
489 int rc = vs->ops.wakeup(vs);
490 async_answer_0(iid, rc);
491}
492
493void graph_visualizer_connection(visualizer_t *vs,
494 ipc_callid_t iid, ipc_call_t *icall, void *arg)
495{
496 ipc_call_t call;
497 ipc_callid_t callid;
[df2e621c]498
[6d5e378]499 /* Claim the visualizer. */
500 if (!cas(&vs->ref_cnt, 0, 1)) {
501 async_answer_0(iid, ELIMIT);
502 return;
503 }
[df2e621c]504
[6d5e378]505 /* Accept the connection. */
506 async_answer_0(iid, EOK);
[df2e621c]507
[6d5e378]508 /* Establish callback session. */
509 callid = async_get_call(&call);
510 vs->notif_sess = async_callback_receive_start(EXCHANGE_SERIALIZE, &call);
[df2e621c]511 if (vs->notif_sess != NULL)
[6d5e378]512 async_answer_0(callid, EOK);
[df2e621c]513 else
[6d5e378]514 async_answer_0(callid, ELIMIT);
[df2e621c]515
[6d5e378]516 /* Enter command loop. */
517 while (true) {
518 callid = async_get_call(&call);
[df2e621c]519
[6d5e378]520 if (!IPC_GET_IMETHOD(call)) {
521 async_answer_0(callid, EINVAL);
522 break;
523 }
[df2e621c]524
[6d5e378]525 switch (IPC_GET_IMETHOD(call)) {
526 case VISUALIZER_CLAIM:
527 vs_claim(vs, callid, &call);
528 break;
529 case VISUALIZER_YIELD:
530 vs_yield(vs, callid, &call);
531 goto terminate;
532 case VISUALIZER_ENUMERATE_MODES:
533 vs_enumerate_modes(vs, callid, &call);
534 break;
535 case VISUALIZER_GET_DEFAULT_MODE:
536 vs_get_default_mode(vs, callid, &call);
537 break;
538 case VISUALIZER_GET_CURRENT_MODE:
539 vs_get_current_mode(vs, callid, &call);
540 break;
541 case VISUALIZER_GET_MODE:
542 vs_get_mode(vs, callid, &call);
543 break;
544 case VISUALIZER_SET_MODE:
545 vs_set_mode(vs, callid, &call);
546 break;
547 case VISUALIZER_UPDATE_DAMAGED_REGION:
548 vs_update_damaged_region(vs, callid, &call);
549 break;
550 case VISUALIZER_SUSPEND:
551 vs_suspend(vs, callid, &call);
552 break;
553 case VISUALIZER_WAKE_UP:
554 vs_wakeup(vs, callid, &call);
555 break;
556 default:
557 async_answer_0(callid, EINVAL);
558 goto terminate;
559 }
560 }
[df2e621c]561
[6d5e378]562terminate:
563 async_hangup(vs->notif_sess);
564 vs->notif_sess = NULL;
565 atomic_set(&vs->ref_cnt, 0);
566}
567
568void graph_renderer_connection(renderer_t *rnd,
569 ipc_callid_t iid, ipc_call_t *icall, void *arg)
570{
571 // TODO
[df2e621c]572
[6d5e378]573 ipc_call_t call;
574 ipc_callid_t callid;
[df2e621c]575
[6d5e378]576 /* Accept the connection. */
577 atomic_inc(&rnd->ref_cnt);
578 async_answer_0(iid, EOK);
[df2e621c]579
[6d5e378]580 /* Enter command loop. */
581 while (true) {
582 callid = async_get_call(&call);
[df2e621c]583
[6d5e378]584 if (!IPC_GET_IMETHOD(call)) {
585 async_answer_0(callid, EINVAL);
586 break;
587 }
[df2e621c]588
[6d5e378]589 switch (IPC_GET_IMETHOD(call)) {
590 default:
591 async_answer_0(callid, EINVAL);
592 goto terminate;
593 }
594 }
[df2e621c]595
[6d5e378]596terminate:
597 atomic_dec(&rnd->ref_cnt);
598}
599
600void graph_client_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
601{
602 /* Find the visualizer or renderer with the given service ID. */
[f9b2cb4c]603 visualizer_t *vs = graph_get_visualizer(IPC_GET_ARG2(*icall));
604 renderer_t *rnd = graph_get_renderer(IPC_GET_ARG2(*icall));
[df2e621c]605
606 if (vs != NULL)
[6d5e378]607 graph_visualizer_connection(vs, iid, icall, arg);
[df2e621c]608 else if (rnd != NULL)
[6d5e378]609 graph_renderer_connection(rnd, iid, icall, arg);
[df2e621c]610 else
[6d5e378]611 async_answer_0(iid, ENOENT);
612}
613
614/** @}
615 */
Note: See TracBrowser for help on using the repository browser.