void found_a_bug(struct ls_state *ls) { if (DECISION_INFO_ONLY == 0) { lsprintf(BUG, COLOUR_BOLD COLOUR_RED "**** A bug was found! ****\n"); lsprintf(BUG, COLOUR_BOLD COLOUR_RED "**** Decision trace follows. ****\n"); } else { lsprintf(ALWAYS, COLOUR_BOLD COLOUR_GREEN "(No bug was found.)\n"); } print_tree_from(ls->save.current, ls->save.next_tid); char *stack = stack_trace(ls->cpu0, ls->eip, ls->sched.cur_agent->tid); lsprintf(BUG, "Stack: %s\n", stack); MM_FREE(stack); PRINT_TREE_INFO(BUG, ls); if (BREAK_ON_BUG) { lsprintf(ALWAYS, COLOUR_BOLD COLOUR_YELLOW "Now giving you the debug prompt. Good luck!\n"); SIM_break_simulation(NULL); } else { SIM_quit(LS_BUG_FOUND); } }
/* returns true if the state changed. */ bool test_update_state(conf_object_t *cpu, struct test_state *t, struct sched_state *s) { if (anybody_alive(cpu, t, s)) { if (!t->test_is_running) { lsprintf(BRANCH, "a test appears to be starting - "); print_qs(BRANCH, s); printf(BRANCH, "\n"); t->test_is_running = true; return true; } } else { if (t->test_is_running) { lsprintf(BRANCH, "a test appears to be ending - "); print_qs(BRANCH, s); printf(BRANCH, "\n"); if (t->current_test) { MM_FREE(t->current_test); t->current_test = NULL; } t->test_is_running = false; return true; } } return false; }
// Save font setting BOOL save_theme_font(APTR file,DOpusCallbackInfo *info,char *type,struct MsgPort *reply_port) { char buf[200],buf2[140],temp[80],*ptr; // Get settings lsprintf(buf,"dopus query font %s",(IPTR)type); DC_CALL5(info, dc_RexxCommand, DC_REGA0, buf, DC_REGA1, buf2, DC_REGD0, sizeof(buf2), DC_REGA2, reply_port, DC_REGD1, 0); //DC_RexxCommand(info,buf,buf2,sizeof(buf2),reply_port,0); //info->dc_RexxCommand(buf,buf2,sizeof(buf2),reply_port,0); // Put result in quotes ptr=buf2; rexx_parse_word(&ptr,temp,78); // Write command to rexx script lsprintf(buf,"\tdopus set font %s \"\'%s\'\"",(IPTR)type,(IPTR)temp); WriteBuf(file,buf,-1); WriteBuf(file,ptr,-1); WriteBuf(file,"\n",1); return 1; }
char* pretty_print_fun(TreeNode* fun, int dd) { assert(fun->type == FUN_TYPE); char* params = pretty_print_list_items(fun->arg0, ", ", 0); char* body = pretty_print_list_items(fun->arg1, ";\n", dd + 1); if (fun->data) { char* rcvr = (char*) fun->data; return lsprintf( "fun (%s) %s(%s)\n%s;;\n", rcvr, fun->name, params, body ); } else { return lsprintf( "fun %s(%s)\n%s;;\n", fun->name, params, body ); } }
void messaging_init(struct messaging_state *state) { #ifdef ID_WRAPPER_MAGIC /* See run_job() in id/job.c for the protocol. Order is important. */ #ifdef OUTPUT_PIPE lsprintf(INFO, "opening output pipe %s\n", OUTPUT_PIPE); state->output_fd = open(OUTPUT_PIPE, O_WRONLY); lsprintf(INFO, "the hatches are open\n"); assert(state->output_fd >= 0 && "opening output pipe failed"); #else STATIC_ASSERT(false && "ID magic but OUTPUT_PIPE not defined"); #endif struct output_message m; m.tag = THUNDERBIRDS_ARE_GO; send(state, &m); #ifdef INPUT_PIPE lsprintf(INFO, "opening input pipe %s\n", INPUT_PIPE); state->input_fd = open(INPUT_PIPE, O_RDONLY); lsprintf(INFO, "aim for the open spot\n"); assert(state->input_fd >= 0 && "opening input pipe failed"); #else STATIC_ASSERT(false && "ID magic but INPUT_PIPE not defined"); #endif #else /* Not running in ID wrapper. Nothing to initialize. */ #endif }
void sched_recover(struct ls_state *ls) { struct sched_state *s = &ls->sched; int tid; assert(ls->just_jumped); if (arbiter_pop_choice(&ls->arbiter, &tid)) { if (tid == s->cur_agent->tid) { /* Hmmmm */ if (kern_timer_entering(ls->eip)) { /* Oops, we ended up trying to leave the thread * we want to be running. Make sure to go * back... */ set_schedule_target(s, s->cur_agent); assert(s->entering_timer); lsprintf(DEV, "Explorer-chosen tid %d wants " "to run; not switching away\n", tid); /* Make sure the arbiter knows this isn't a * voluntary reschedule. The handling_timer flag * won't be on now, but sched_update sets it. */ lsprintf(INFO, "Updating the last_agent: "); print_agent(INFO, s->last_agent); printf(INFO, " to "); print_agent(INFO, s->cur_agent); printf(INFO, "\n"); s->last_agent = s->cur_agent; /* This will cause an assert to trip faster. */ s->voluntary_resched_tid = -1; } else { lsprintf(INFO, "Explorer-chosen tid %d already " "running!\n", tid); } } else { // TODO: duplicate agent search logic (arbiter.c) struct agent *a = agent_by_tid_or_null(&s->rq, tid); if (a == NULL) { a = agent_by_tid_or_null(&s->sq, tid); } assert(a != NULL && "bogus explorer-chosen tid!"); lsprintf(DEV, "Recovering to explorer-chosen tid %d from " "tid %d\n", tid, s->cur_agent->tid); set_schedule_target(s, a); /* Hmmmm */ if (!kern_timer_entering(ls->eip)) { ls->eip = cause_timer_interrupt_immediately( ls->cpu0); } s->entering_timer = true; } } else { tid = s->cur_agent->tid; lsprintf(BUG, "Explorer chose no tid; defaulting to %d\n", tid); } save_recover(&ls->save, ls, tid); }
/* * Common function for prompting for shell/pipe command, and for recording the * last shell/pipe command so that we can support "!!" convention. * * Note that for 'pipecmd()', we must retain a leading "!". */ static int ShellPrompt( TBUFF **holds, char *result, int rerun) /* TRUE/FALSE: spawn, -TRUE: pipecmd */ { register int s; register SIZE_T len; static const char bang[] = SHPIPE_LEFT; BUFFER *bp; int cb = any_changed_buf(&bp), fix = (rerun != -TRUE); char save[NLINE], temp[NLINE], line[NLINE+1]; if ((len = tb_length(*holds)) != 0) { (void)strncpy(save, tb_values(*holds), len); } save[len] = EOS; /* if it doesn't start with '!', or if that's all it is */ if (!isShellOrPipe(save) || save[1] == EOS) (void)strcpy(save, bang); (void)strcpy(line, save); if (rerun != TRUE) { if (cb != 0) { if (cb > 1) { (void)lsprintf(temp, "Warning: %d modified buffers: %s", cb, bang); } else { (void)lsprintf(temp, "Warning: buffer \"%s\" is modified: %s", bp->b_bname, bang); } } else { (void)lsprintf(temp, "%s%s", rerun == -TRUE ? "" : ": ", bang); } if ((s = mlreply_no_bs(temp, line+1, NLINE)) != TRUE) return s; } if (line[1] == EOS) return FALSE; *holds = tb_scopy(holds, line); (void)strcpy(result, line+fix); return TRUE; }
// Display colours string void _config_env_screenmode_show_depth(config_env_data *data) { char string[20]; // Build display string if (data->config->screen_depth<=16) lsprintf(string,"%ld",1<<data->config->screen_depth); else lsprintf(string,"%ld bit",data->config->screen_depth); // Show string SetGadgetValue(data->option_list,GAD_ENVIRONMENT_COLORS_DISPLAY,(ULONG)string); }
static struct agent *agent_by_tid(struct agent_q *q, int tid) { struct agent *a = agent_by_tid_or_null(q, tid); if (a == NULL) { conf_object_t *cpu = SIM_get_object("cpu0"); char *stack = stack_trace(cpu, GET_CPU_ATTR(cpu, eip), -1); lsprintf(ALWAYS, COLOUR_BOLD COLOUR_RED "TID %d isn't in the " "right queue; probably incorrect annotations?\n", tid); lsprintf(ALWAYS, COLOUR_BOLD COLOUR_RED "Current stack: %s\n" COLOUR_DEFAULT, stack); assert(0); } return a; }
/* The user mem heap tracking can only work for a single address space. We want * to pay attention to the userspace program under test, not the shell or init * or idle or anything like that. Figure out what that process's cr3 is. */ static bool ignore_user_access(struct ls_state *ls) { unsigned int current_tid = ls->sched.cur_agent->tid; unsigned int cr3 = GET_CPU_ATTR(ls->cpu0, cr3);; if (!testing_userspace()) { /* Don't attempt to track user accesses for kernelspace tests. * Tests like vanish_vanish require multiple user cr3s, which * we don't support when tracking user accesses. When doing a * userspace test, we need to do the below cr3 assertion, but * when doing a kernel test we cannot, so instead we have to * ignore all user accesses entirely. */ return true; } else if (current_tid == kern_get_init_tid() || current_tid == kern_get_shell_tid() || (kern_has_idle() && current_tid == kern_get_idle_tid())) { return true; } else if (ls->user_mem.cr3 == USER_CR3_WAITING_FOR_THUNDERBIRDS) { ls->user_mem.cr3 = USER_CR3_WAITING_FOR_EXEC; ls->user_mem.cr3_tid = current_tid; return true; } else if (ls->user_mem.cr3 == USER_CR3_WAITING_FOR_EXEC) { /* must wait for a trip through kernelspace; see below */ return true; } else if (ls->user_mem.cr3 == USER_CR3_EXEC_HAPPENED) { /* recognized non-shell-non-idle-non-init user process has been * through exec and back. hopefully its new cr3 is permanent. */ assert(cr3 != USER_CR3_WAITING_FOR_EXEC); assert(cr3 != USER_CR3_EXEC_HAPPENED); ls->user_mem.cr3 = cr3; lsprintf(DEV, "Registered cr3 value 0x%x for userspace " "tid %d.\n", cr3, current_tid); return false; } else if (ls->user_mem.cr3 != cr3) { lsprintf(ALWAYS, COLOUR_BOLD COLOUR_RED "Memory tracking for " "more than 1 user address space is unsupported!\n"); lsprintf(ALWAYS, COLOUR_BOLD COLOUR_RED "Already tracking for " "cr3 0x%x, belonging to tid %d; current cr3 0x%x, " "current tid %d\n", ls->user_mem.cr3, ls->user_mem.cr3_tid, cr3, current_tid); lsprintf(ALWAYS, COLOUR_BOLD COLOUR_RED "If you're trying to " "run vanish_vanish, make sure TESTING_USERSPACE=0.\n"); lsprintf(ALWAYS, COLOUR_BOLD COLOUR_RED "Otherwise, make sure " "your test case doesn't fork().\n" COLOUR_DEFAULT); assert(0); return false; } else { return false; } }
static void mem_enter_free(struct ls_state *ls, bool in_kernel, unsigned int base) { struct mem_state *m = in_kernel ? &ls->kern_mem : &ls->user_mem; struct chunk *chunk; assert(!m->in_mm_init); if (m->in_alloc || m->in_free) { FOUND_A_BUG(ls, "Free (in %s) reentered %s!", K_STR(in_kernel), m->in_alloc ? "Malloc" : "Free"); } chunk = remove_chunk(&m->heap, base); if (base == 0) { assert(chunk == NULL); lsprintf(INFO, "Free() NULL (in %s); ok, I guess...\n", K_STR(in_kernel)); } else if (chunk == NULL) { struct hax *before; struct hax *after; chunk = find_freed_chunk(ls, base, in_kernel, &before, &after); if (chunk != NULL) { print_freed_chunk_info(chunk, before, after, NULL); char buf[BUF_SIZE]; int len = scnprintf(buf, BUF_SIZE, "DOUBLE FREE (in %s)" " of 0x%x!", K_STR(in_kernel), base); FOUND_A_BUG_HTML_INFO(ls, buf, len, html_env, print_freed_chunk_info(chunk, before, after, html_env); ); } else {
char* pretty_print_unop(TreeNode* unop, int dd) { assert(unop->type == UNOP_TYPE); char* arg0 = pretty_print_any(unop->arg0, dd); return lsprintf("(%s %s)", unop->name, arg0); }
// Add an entry to the filetype list Att_Node *filetype_add_entry( config_filetypes_data *data, Cfg_Filetype *type) { char buf[44]; FiletypeNode *node; Att_Node *new_node; // Allocate new node if ((node=AllocVec(sizeof(FiletypeNode),MEMF_CLEAR))) { // Fill out node node->type=type; // Build display entry lsprintf(buf,"%s\t%s",type->type.name,type->type.id); // Add entry to list for this filetype if ((new_node=Att_NewNode(data->filetype_list,buf,(ULONG)node,ADDNODE_SORT))) return new_node; FreeVec(node); } return 0; }
char* pretty_print_dot(TreeNode* dot, int dd) { assert(dot->type == DOT_TYPE); char* arg0 = pretty_print_any(dot->arg0, dd); return lsprintf("%s.%s", arg0, dot->name); }
char* pretty_print_list(TreeNode* list, int dd) { assert(list->type == LIST_TYPE); char* items = pretty_print_list_items(list, ", ", 0); return lsprintf("[%s]", items); }
/* html env may be null */ static void print_freed_chunk_info(struct chunk *c, struct hax *before, struct hax *after, struct fab_html_env *html_env) { char allocated_msg[BUF_SIZE]; char freed_msg[BUF_SIZE]; unsigned int pos = 0; scnprintf(allocated_msg, BUF_SIZE, "Heap block [0x%x | %d] " "was allocated at:", c->base, c->len); pos += scnprintf(freed_msg + pos, BUF_SIZE - pos, "...and, between preemptions "); if (after == NULL) { pos += scnprintf(freed_msg + pos, BUF_SIZE - pos, "[root]"); } else { pos += scnprintf(freed_msg + pos, BUF_SIZE - pos, "#%d/tid%d", after->depth, after->chosen_thread); } pos += scnprintf(freed_msg + pos, BUF_SIZE - pos, " and "); if (before == NULL) { pos += scnprintf(freed_msg + pos, BUF_SIZE - pos, "[latest]"); } else { pos += scnprintf(freed_msg + pos, BUF_SIZE - pos, "#%d/tid%d", before->depth, before->chosen_thread); } pos += scnprintf(freed_msg + pos, BUF_SIZE - pos, ", freed at:"); if (html_env == NULL) { lsprintf(BUG, "%s", allocated_msg); print_stack_trace(BUG, c->malloc_trace); printf(BUG, "\n"); lsprintf(BUG, "%s", freed_msg); print_stack_trace(BUG, c->free_trace); printf(BUG, "\n"); } else { HTML_PRINTF(html_env, "%s" HTML_NEWLINE, allocated_msg); HTML_PRINTF(html_env, "TID %d at:" HTML_NEWLINE, c->malloc_trace->tid); HTML_PRINT_STACK_TRACE(html_env, c->malloc_trace); HTML_PRINTF(html_env, HTML_NEWLINE HTML_NEWLINE); HTML_PRINTF(html_env, "%s" HTML_NEWLINE, freed_msg); HTML_PRINTF(html_env, "TID %d at:" HTML_NEWLINE, c->free_trace->tid); HTML_PRINT_STACK_TRACE(html_env, c->free_trace); HTML_PRINTF(html_env, HTML_NEWLINE); } }
char* pretty_print_binop(TreeNode* binop, int dd) { assert(binop->type == BINOP_TYPE); char* arg0 = pretty_print_any(binop->arg0, dd); char* arg1 = pretty_print_any(binop->arg1, dd); return lsprintf("(%s %s %s)", arg0, binop->name, arg1); }
char* pretty_print_bind(TreeNode* bind, int dd) { assert(bind->type == BIND_TYPE); char* symb = bind->name; char* arg0 = pretty_print_any(bind->arg0, dd); return lsprintf("%s = %s", symb, arg0); }
char* pretty_print_call(TreeNode* call, int dd) { assert(call->type == CALL_TYPE); char* fun = pretty_print_any(call->arg0, dd); char* args = pretty_print_list_items(call->arg1, ", ", 0); return lsprintf("%s(%s)", fun, args); }
char* pretty_print_lambda(TreeNode* lambda, int dd) { assert(lambda->type == LAMBDA_TYPE); char* params = pretty_print_list_items(lambda->arg0, ", ", 0); char* body = pretty_print_list_items(lambda->arg1, ";\n", dd + 1); return lsprintf("|%s| ->\n%s%s;;\n", params, body, indent(dd)); }
// Test if a serial number is valid BOOL LIBFUNC L_SerialValid(REG(a0, serial_data *data)) { long nnnnn; long xxxx; long yyyy; long AAAAA; long temp; char buf[16]; // Get serial number and random key nnnnn=~data->serial_number; xxxx=data->random_key; #ifdef KRAUT if (nnnnn<SERIAL_MIN || nnnnn>=SERIAL_MAX || nnnnn==444730) return 0; #endif // Calculate yyyy temp=xxxx+nnnnn; yyyy=temp*temp*temp; // Build yyyy string lsprintf(buf,"%ld",yyyy); // Compare last four digits temp=strlen(buf); if (temp>4) temp-=4; else temp=0; if (strcmp(buf+temp,data->serial_check_2)) { return 0; } // Calculate AAAAA AAAAA=xxxx+yyyy; AAAAA*=AAAAA; temp=nnnnn*nnnnn*nnnnn; AAAAA*=temp; // Build AAAAA string L_Ito26(AAAAA,buf); // Pad with A's to 5 digits temp=strlen(buf); while (temp++<5) strcat(buf,"A"); // Compare last 5 digits temp=strlen(buf); if (temp>5) temp-=5; else temp=0; if (strcmp(buf+temp,data->serial_check_1)) { return 0; } return 1; }
char* pretty_print_call_lambda(TreeNode* call, int dd) { assert(call->type == CALL_LAMBDA_TYPE); char* fun = pretty_print_any(call->arg0, dd); char* args = pretty_print_list_items(call->arg1, ", ", 0); char* lamb = pretty_print_lambda(call->arg2, dd); return lsprintf("%s(%s) %s", fun, args, lamb); }
/* * Returns the special string consisting of program name + version, used to * fill in the filename-field for scratch buffers that are not associated with * an external file. */ const char * non_filename(void) { static char buf[80]; if (buf[0] == EOS) (void) lsprintf(buf, " %s %s%s", prognam, version, VILE_PATCHLEVEL); return buf; }
// Display mode support void _config_env_screenmode_init(config_env_data *data,BOOL refresh) { struct NameInfo nameinfo; int sel; // Get current mode switch (data->config->screen_mode) { case MODE_WORKBENCHUSE: stccpy( data->mode_name, GetString(locale,MSG_ENVIRONMENT_SCREENMODE_WB_USE), sizeof(data->mode_name)); break; case MODE_WORKBENCHCLONE: stccpy( data->mode_name, GetString(locale,MSG_ENVIRONMENT_SCREENMODE_WB_CLONE), sizeof(data->mode_name)); break; case MODE_PUBLICSCREEN: lsprintf(data->mode_name,"%s:%s", data->config->pubscreen_name, GetString(locale,MSG_USE)); break; default: // Get mode name (if available) if (GetDisplayInfoData( 0, (char *)&nameinfo, sizeof(nameinfo), DTAG_NAME, data->config->screen_mode)) { stccpy(data->mode_name,nameinfo.Name,sizeof(data->mode_name)); } else { data->mode_name[0]=0; } break; } // Set current mode selection if (refresh) { sel=Att_NodeNumber(data->mode_list,data->mode_name); SetGadgetValue(data->option_list,GAD_ENVIRONMENT_SCREENMODE_MODE,sel); } // Initialise mode data _config_env_screenmode_init_mode(data,refresh); }
static void handle_vanish(struct sched_state *s) { if (ACTION(s, vanishing) && !HANDLING_INTERRUPT(s)) { lsprintf(DEV, "agent %d vanish -- ", s->cur_agent->tid); print_qs(DEV, s); printf(DEV, "\n"); agent_vanish(s); /* the vanishing flag stays on (TODO: is it needed?) */ } }
// Edit a filetype action void filetypeed_edit_action( filetype_ed_data *data, short action, char *name) { Cfg_Function *function; FunctionStartup *startup; BOOL success=0; // Allocate startup data if (!(startup=AllocVec(sizeof(FunctionStartup),MEMF_CLEAR))) return; // Find the function we want to edit if (!(function=FindFunctionType(&data->type->function_list,action))) { // Allocate a new function function=NewFunction(0,action); } // Otherwise, copy the function else function=CopyFunction(function,0,0); // Valid function? if (function) { // Copy startup data *startup=data->func_startup; startup->window=data->window; startup->function=function; startup->owner_ipc=data->ipc; startup->object=data->type; startup->object_flags=action; // Build title lsprintf(startup->title,"%s : %s",data->type->type.name,name); // Launch editor if ((IPC_Launch( &data->proc_list, &data->editor[action], "dopus_function_editor", (ULONG)IPC_NATIVE(FunctionEditor), STACK_DEFAULT, (ULONG)startup, (struct Library *)DOSBase)) && data->editor[action]) success=1; } // Free data if not successful if (!success) { FreeFunction(function); FreeVec(startup); } }
// Save pens setting BOOL save_theme_pens(APTR file,DOpusCallbackInfo *info,char *type,struct MsgPort *reply_port) { char buf[150],buf2[100]; // Get settings lsprintf(buf,"dopus query pens %s",(IPTR)type); DC_CALL5(info, dc_RexxCommand, DC_REGA0, buf, DC_REGA1, buf2, DC_REGD0, sizeof(buf2), DC_REGA2, reply_port, DC_REGD1, 0); //DC_RexxCommand(info,buf,buf2,sizeof(buf2),reply_port,0); //info->dc_RexxCommand(buf,buf2,sizeof(buf2),reply_port,0); // Write command to rexx script lsprintf(buf,"\tdopus set pens %s %s\n",(IPTR)type,(IPTR)buf2); WriteBuf(file,buf,-1); return 1; }
/* A more user-friendly way of asserting NO_ACTION. */ static void assert_no_action(struct sched_state *s, const char *new_act) { bool failed = false; CHECK_NOT_ACTION(failed, s, handling_timer); CHECK_NOT_ACTION(failed, s, context_switch); CHECK_NOT_ACTION(failed, s, forking); CHECK_NOT_ACTION(failed, s, sleeping); CHECK_NOT_ACTION(failed, s, vanishing); CHECK_NOT_ACTION(failed, s, readlining); if (failed) { conf_object_t *cpu = SIM_get_object("cpu0"); char *stack = stack_trace(cpu, GET_CPU_ATTR(cpu, eip), s->cur_agent->tid); lsprintf(ALWAYS, COLOUR_BOLD COLOUR_RED "While trying to do %s;" " probably incorrect annotations?\n", new_act); lsprintf(ALWAYS, COLOUR_BOLD COLOUR_RED "Current stack: %s\n", stack); assert(0); } }
char* pretty_print_any(TreeNode* node, int dd) { int64_t vv_i; double vv_d; switch (node->type) { case INTEGER_TYPE: vv_i = *((int64_t*)node->data); return lsprintf("%ld", vv_i); case FLOAT_TYPE: vv_d = *((double*)node->data); return lsprintf("%f", vv_d); case SYMBOL_TYPE: return (char*)node->data; case STRING_TYPE: return pretty_print_string(node, dd); case BINOP_TYPE: return pretty_print_binop(node, dd); case UNOP_TYPE: return pretty_print_unop(node, dd); case DOT_TYPE: return pretty_print_dot(node, dd); case BIND_TYPE: return pretty_print_bind(node, dd); case LIST_TYPE: return pretty_print_list(node, dd); case FUN_TYPE: return pretty_print_fun(node, dd); case LAMBDA_TYPE: return pretty_print_lambda(node, dd); case CALL_TYPE: return pretty_print_call(node, dd); case CALL_LAMBDA_TYPE: return pretty_print_call_lambda(node, dd); default: carp("pretty_print_tree(): Bad node type."); } abort(); }
static void paint(void *widget, OsdPainter *painter, int x, int y) { WgCompass *self = widget; osdPainterTranslate(painter, x, y); osdPainterSetPen(painter, &scalePen); // osdPainterDrawLine(painter, -SCALE_LENGTH / 2, 0, SCALE_LENGTH / 2, 0); // draw scale int rem = self->heading % SCALE; int xoffset = (int)(((float)rem / (float)SCALE) * (float)LONG_NOTCH_SPACE); int xscale0 = (-(SCALE_LENGTH / 2) / LONG_NOTCH_SPACE - 1) * LONG_NOTCH_SPACE - xoffset; int xscale = xscale0; int deg = (+(self->heading / SCALE) -(SCALE_LENGTH / 2) / LONG_NOTCH_SPACE - 1) * SCALE; osdPainterSetFont(painter, osdFontByName(captionFont)); while(xscale <= (SCALE_LENGTH / 2)) { bool longNotch = ((xscale0 - xscale) % LONG_NOTCH_SPACE == 0); if ((xscale >= (-SCALE_LENGTH / 2)) && (xscale <= (SCALE_LENGTH / 2))) { if (longNotch) { // draw long notch osdPainterSetPen(painter, &scalePen); osdPainterDrawLine(painter, xscale, 0, xscale, -LONG_NOTCH_LEN); osdPainterSetPen(painter, &scalePenInv); osdPainterDrawLine(painter, xscale + 1, 0, xscale + 1, -LONG_NOTCH_LEN); // draw caption osdPainterSetPen(painter, &captionTextPen); osdPainterDrawText(painter, xscale - 2, -LONG_NOTCH_LEN - 10, deg2WorldSide(deg)); } else { // draw short notch osdPainterSetPen(painter, &scalePen); osdPainterDrawLine(painter, xscale, 0, xscale, -SHORT_NOTCH_LEN); osdPainterSetPen(painter, &scalePenInv); osdPainterDrawLine(painter, xscale + 1, 0, xscale + 1, -SHORT_NOTCH_LEN); } } if (longNotch) deg += SCALE; xscale += SHORT_NOTCH_SPACE; } // draw heading value char str[5]; lsprintf(str, "%03d", self->heading); osdPainterSetFont(painter, osdFontByName(headingFont)); osdPainterSetPen(painter, &headingTextPen); int width = 0, height = 0; osdPainterTextBounds(painter, str, &width, &height); osdPainterDrawText(painter, -width / 2, 10, str); // draw arrow osdPainterSetPen(painter, &arrowPen); osdPainterSetBrush(painter, &arrowBrush); osdPainterDrawPoly(painter, arrowPoly, 3); }