void divide_inf(struct packet_info sniffer[],struct timespec th, struct timespec te, struct timespec dmaci,int retry,int ampdu_type,int t){ struct timespec tr={0},busywait={0},overall_busywait={0},inf={0},tmp1={0},tmp2={0}; int j,jump,flag=0; int bj,ej = -1; //first round jump = 0; j = current_index[t]; for (;; j=(j-1+HOLD_TIME)%HOLD_TIME){ jump = jump + 1; if (jump == HOLD_TIME){ break; } clear_timespec(&tr); tr = sniffer[j].te; if ((timespec_compare(&tr,&th)>0) && (timespec_compare(&tr ,&te)<0)){ if (flag == 0){ bj = j; flag = 1; } ej = j; clear_timespec(&busywait); busywait = cal_transmit_time(sniffer[j].len,sniffer[j].phy_rate); if (retry == 0){ overall_busywait = timespec_add(overall_busywait ,busywait); } summary[t].inf_packets = summary[t].inf_packets + 1; summary[t].inf_bytes = summary[t].inf_bytes + sniffer[j].len; } if ( timespec_compare(&tr,&th)<0){ break; } } //second round jump = 0; for (j =current_index[t];; j=(j-1+HOLD_TIME)%HOLD_TIME){ jump = jump +1; if (jump == HOLD_TIME){ break; } clear_timespec(&tr); tr=sniffer[j].te; if ((timespec_compare(&tr,&th)>0) && (timespec_compare(&tr ,&te)<0)){ clear_timespec(&busywait); busywait = cal_transmit_time(sniffer[j].len,sniffer[j].phy_rate); int ratio = 100*(busywait.tv_sec*1000000000+busywait.tv_nsec)/(overall_busywait.tv_sec*1000000000+overall_busywait.tv_nsec); clear_timespec(&inf); inf.tv_sec = dmaci.tv_sec*ratio/100; inf.tv_nsec = dmaci.tv_nsec*ratio/100; //checkpoint if (inf.tv_sec < 0 || inf.tv_nsec < 0){ inf.tv_sec = 0; inf.tv_nsec =0; } if ( retry == 0){ update_list(sniffer[j].wlan_src,sniffer[j].wlan_dst,inf,t); } } if ( timespec_compare(&tr,&th)<0){ break; } } }
static __inline struct timespec timeval2spec(const struct timeval *a) { struct timespec ts = { .tv_sec = a->tv_sec, .tv_nsec = a->tv_usec * 1000 }; return ts; } static __inline struct timeval timespec2val(const struct timespec *a) { struct timeval tv = { .tv_sec = a->tv_sec, .tv_usec = a->tv_nsec / 1000 }; return tv; } static __inline struct timespec timespec_add(struct timespec a, struct timespec b) { struct timespec ret = { a.tv_sec + b.tv_sec, a.tv_nsec + b.tv_nsec }; if (ret.tv_nsec >= 1000000000) { ret.tv_sec++; ret.tv_nsec -= 1000000000; } return ret; } static __inline struct timespec timespec_sub(struct timespec a, struct timespec b) { struct timespec ret = { a.tv_sec - b.tv_sec, a.tv_nsec - b.tv_nsec }; if (ret.tv_nsec < 0) { ret.tv_sec--; ret.tv_nsec += 1000000000; } return ret; } /* * wait until ts, either busy or sleeping if more than 1ms. * Return wakeup time. */ static struct timespec wait_time(struct timespec ts) { for (;;) { struct timespec w, cur; clock_gettime(CLOCK_REALTIME_PRECISE, &cur); w = timespec_sub(ts, cur); if (w.tv_sec < 0) return cur; else if (w.tv_sec > 0 || w.tv_nsec > 1000000) poll(NULL, 0, 1); } } static void * sender_body(void *data) { struct targ *targ = (struct targ *) data; struct pollfd fds[1]; struct netmap_if *nifp = targ->nifp; struct netmap_ring *txring; int i, n = targ->g->npackets / targ->g->nthreads, sent = 0; int options = targ->g->options | OPT_COPY; struct timespec nexttime = { 0, 0}; // XXX silence compiler int rate_limit = targ->g->tx_rate; struct pkt *pkt = &targ->pkt; void *frame; int size; frame = pkt; frame += sizeof(pkt->vh) - targ->g->virt_header; size = targ->g->pkt_size + targ->g->virt_header; D("start"); if (setaffinity(targ->thread, targ->affinity)) goto quit; /* setup poll(2) mechanism. */ memset(fds, 0, sizeof(fds)); fds[0].fd = targ->fd; fds[0].events = (POLLOUT); /* main loop.*/ clock_gettime(CLOCK_REALTIME_PRECISE, &targ->tic); if (rate_limit) { targ->tic = timespec_add(targ->tic, (struct timespec){2,0}); targ->tic.tv_nsec = 0; wait_time(targ->tic); nexttime = targ->tic; } if (targ->g->dev_type == DEV_TAP) { D("writing to file desc %d", targ->g->main_fd); for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) { if (write(targ->g->main_fd, frame, size) != -1) sent++; update_addresses(pkt, targ->g); if (i > 10000) { targ->count = sent; i = 0; } } #ifndef NO_PCAP } else if (targ->g->dev_type == DEV_PCAP) { pcap_t *p = targ->g->p; for (i = 0; !targ->cancel && (n == 0 || sent < n); i++) { if (pcap_inject(p, frame, size) != -1) sent++; update_addresses(pkt, targ->g); if (i > 10000) { targ->count = sent; i = 0; } } #endif /* NO_PCAP */ } else { int tosend = 0; int frags = targ->g->frags; while (!targ->cancel && (n == 0 || sent < n)) { if (rate_limit && tosend <= 0) { tosend = targ->g->burst; nexttime = timespec_add(nexttime, targ->g->tx_period); wait_time(nexttime); } /* * wait for available room in the send queue(s) */ if (poll(fds, 1, 2000) <= 0) { if (targ->cancel) break; D("poll error/timeout on queue %d: %s", targ->me, strerror(errno)); goto quit; } if (fds[0].revents & POLLERR) { D("poll error"); goto quit; } /* * scan our queues and send on those with room */ if (options & OPT_COPY && sent > 100000 && !(targ->g->options & OPT_COPY) ) { D("drop copy"); options &= ~OPT_COPY; } for (i = targ->qfirst; i < targ->qlast; i++) { int m, limit = rate_limit ? tosend : targ->g->burst; if (n > 0 && n - sent < limit) limit = n - sent; txring = NETMAP_TXRING(nifp, i); if (nm_ring_empty(txring)) continue; if (frags > 1) limit = ((limit + frags - 1) / frags) * frags; m = send_packets(txring, pkt, frame, size, targ->g, limit, options, frags); ND("limit %d tail %d frags %d m %d", limit, txring->tail, frags, m); sent += m; targ->count = sent; if (rate_limit) { tosend -= m; if (tosend <= 0) break; } } } /* flush any remaining packets */ ioctl(fds[0].fd, NIOCTXSYNC, NULL); /* final part: wait all the TX queues to be empty. */ for (i = targ->qfirst; i < targ->qlast; i++) { txring = NETMAP_TXRING(nifp, i); while (nm_tx_pending(txring)) { ioctl(fds[0].fd, NIOCTXSYNC, NULL); usleep(1); /* wait 1 tick */ } } } /* end DEV_NETMAP */ clock_gettime(CLOCK_REALTIME_PRECISE, &targ->toc); targ->completed = 1; targ->count = sent; quit: /* reset the ``used`` flag. */ targ->used = 0; return (NULL); } #ifndef NO_PCAP static void receive_pcap(u_char *user, const struct pcap_pkthdr * h, const u_char * bytes) { int *count = (int *)user; (void)h; /* UNUSED */ (void)bytes; /* UNUSED */ (*count)++; }
/* * Calculate a second-aligned starting time for the packet stream. Busy * wait between our calculated interval and dropping the provided packet * into the socket. If we hit our duration limit, bail. * We sweep the ports from a->port to a->port_max included. * If the two ports are the same we connect() the socket upfront, which * almost halves the cost of the sendto() call. */ static int timing_loop(struct _a *a) { struct timespec nexttime, starttime, tmptime; long long waited; u_int32_t counter; long finishtime; long send_errors, send_calls; /* do not call gettimeofday more than every 20us */ long minres_ns = 200000; int ic, gettimeofday_cycles; int cur_port; uint64_t n, ns; if (clock_getres(CLOCK_REALTIME, &tmptime) == -1) { perror("clock_getres"); return (-1); } ns = a->interval.tv_nsec; if (timespec_ge(&tmptime, &a->interval)) fprintf(stderr, "warning: interval (%jd.%09ld) less than resolution (%jd.%09ld)\n", (intmax_t)a->interval.tv_sec, a->interval.tv_nsec, (intmax_t)tmptime.tv_sec, tmptime.tv_nsec); /* interval too short, limit the number of gettimeofday() * calls, but also make sure there is at least one every * some 100 packets. */ if ((long)ns < minres_ns/100) gettimeofday_cycles = 100; else gettimeofday_cycles = minres_ns/ns; fprintf(stderr, "calling time every %d cycles\n", gettimeofday_cycles); if (clock_gettime(CLOCK_REALTIME, &starttime) == -1) { perror("clock_gettime"); return (-1); } tmptime.tv_sec = 2; tmptime.tv_nsec = 0; timespec_add(&starttime, &tmptime); starttime.tv_nsec = 0; if (wait_time(starttime, NULL, NULL) == -1) return (-1); nexttime = starttime; finishtime = starttime.tv_sec + a->duration; send_errors = send_calls = 0; counter = 0; waited = 0; ic = gettimeofday_cycles; cur_port = a->port; if (a->port == a->port_max) { if (a->ipv6) { if (connect(a->s, (struct sockaddr *)&a->sin6, sizeof(a->sin6))) { perror("connect (ipv6)"); return (-1); } } else { if (connect(a->s, (struct sockaddr *)&a->sin, sizeof(a->sin))) { perror("connect (ipv4)"); return (-1); } } } while (1) { int ret; timespec_add(&nexttime, &a->interval); if (--ic <= 0) { ic = gettimeofday_cycles; if (wait_time(nexttime, &tmptime, &waited) == -1) return (-1); } /* * We maintain and, if there's room, send a counter. Note * that even if the error is purely local, we still increment * the counter, so missing sequence numbers on the receive * side should not be assumed to be packets lost in transit. * For example, if the UDP socket gets back an ICMP from a * previous send, the error will turn up the current send * operation, causing the current sequence number also to be * skipped. * The counter is incremented only on the initial port number, * so all destinations will see the same set of packets. */ if (cur_port == a->port && a->packet_len >= 4) { be32enc(a->packet, counter); counter++; } if (a->port == a->port_max) { /* socket already bound */ ret = send(a->s, a->packet, a->packet_len, 0); } else { a->sin.sin_port = htons(cur_port++); if (cur_port > a->port_max) cur_port = a->port; if (a->ipv6) { ret = sendto(a->s, a->packet, a->packet_len, 0, (struct sockaddr *)&a->sin6, sizeof(a->sin6)); } else { ret = sendto(a->s, a->packet, a->packet_len, 0, (struct sockaddr *)&a->sin, sizeof(a->sin)); } } if (ret < 0) send_errors++; send_calls++; if (a->duration != 0 && tmptime.tv_sec >= finishtime) goto done; } done: if (clock_gettime(CLOCK_REALTIME, &tmptime) == -1) { perror("clock_gettime"); return (-1); } printf("\n"); printf("start: %jd.%09ld\n", (intmax_t)starttime.tv_sec, starttime.tv_nsec); printf("finish: %jd.%09ld\n", (intmax_t)tmptime.tv_sec, tmptime.tv_nsec); printf("send calls: %ld\n", send_calls); printf("send errors: %ld\n", send_errors); printf("approx send rate: %ld pps\n", (send_calls - send_errors) / a->duration); n = send_calls - send_errors; if (n > 0) { ns = (tmptime.tv_sec - starttime.tv_sec) * 1000000000UL + (tmptime.tv_nsec - starttime.tv_nsec); n = ns / n; } printf("time/packet: %u ns\n", (u_int)n); printf("approx error rate: %ld\n", (send_errors / send_calls)); printf("waited: %lld\n", waited); printf("approx waits/sec: %lld\n", (long long)(waited / a->duration)); printf("approx wait rate: %lld\n", (long long)(waited / send_calls)); return (0); }
static void *cpu_thread(void *dummyptr) { assert(pthread_self() == cpu_thread_id); LOG("cpu_thread : initialized..."); struct timespec deltat; #if !MOBILE_DEVICE struct timespec disk_motor_time; #endif struct timespec t0; // the target timer struct timespec ti, tj; // actual time samples bool negative = false; long drift_adj_nsecs = 0; // generic drift adjustment between target and actual int debugging_cycles0 = 0; int debugging_cycles = 0; #if DEBUG_TIMING unsigned long dbg_ticks = 0; int speaker_neg_feedback = 0; int speaker_pos_feedback = 0; unsigned int dbg_cycles_executed = 0; #endif do { #ifdef AUDIO_ENABLED LOG("CPUTHREAD %lu LOCKING FOR MAYBE INITIALIZING AUDIO ...", cpu_thread_id); pthread_mutex_lock(&interface_mutex); if (emul_reinitialize_audio) { emul_reinitialize_audio = false; speaker_destroy(); MB_Destroy(); audio_shutdown(); audio_init(); speaker_init(); MB_Initialize(); } pthread_mutex_unlock(&interface_mutex); LOG("UNLOCKING FOR MAYBE INITIALIZING AUDIO ..."); #endif if (emul_reinitialize) { reinitialize(); } LOG("cpu_thread : begin main loop ..."); clock_gettime(CLOCK_MONOTONIC, &t0); do { SCOPE_TRACE_CPU("CPU mainloop"); // -LOCK----------------------------------------------------------------------------------------- SAMPLE ti #ifdef AUDIO_ENABLED if (UNLIKELY(emul_pause_audio)) { emul_pause_audio = false; audio_pause(); } #endif pthread_mutex_lock(&interface_mutex); #ifdef AUDIO_ENABLED if (UNLIKELY(emul_resume_audio)) { emul_resume_audio = false; audio_resume(); } #endif clock_gettime(CLOCK_MONOTONIC, &ti); deltat = timespec_diff(t0, ti, &negative); if (deltat.tv_sec) { if (!is_fullspeed) { TIMING_LOG("NOTE : serious divergence from target time ..."); } t0 = ti; deltat = timespec_diff(t0, ti, &negative); } t0 = timespec_add(t0, EXECUTION_PERIOD_NSECS); // expected interval drift_adj_nsecs = negative ? ~deltat.tv_nsec : deltat.tv_nsec; // set up increment & decrement counters cpu65_cycles_to_execute = (cycles_persec_target / 1000); // cycles_persec_target * EXECUTION_PERIOD_NSECS / NANOSECONDS_PER_SECOND if (!is_fullspeed) { cpu65_cycles_to_execute += cycles_speaker_feedback; } if (cpu65_cycles_to_execute < 0) { cpu65_cycles_to_execute = 0; } #ifdef AUDIO_ENABLED MB_StartOfCpuExecute(); #endif if (is_debugging) { debugging_cycles0 = cpu65_cycles_to_execute; debugging_cycles = cpu65_cycles_to_execute; } do { if (is_debugging) { cpu65_cycles_to_execute = 1; } cpu65_cycle_count = 0; cycles_checkpoint_count = 0; cpu65_run(); // run emulation for cpu65_cycles_to_execute cycles ... if (is_debugging) { debugging_cycles -= cpu65_cycle_count; if (c_debugger_should_break() || (debugging_cycles <= 0)) { int err = 0; if ((err = pthread_cond_signal(&dbg_thread_cond))) { ERRLOG("pthread_cond_signal : %d", err); } if ((err = pthread_cond_wait(&cpu_thread_cond, &interface_mutex))) { ERRLOG("pthread_cond_wait : %d", err); } if (debugging_cycles <= 0) { cpu65_cycle_count = debugging_cycles0 - debugging_cycles/*<=0*/; break; } } } if (emul_reinitialize) { reinitialize(); } } while (is_debugging); #if DEBUG_TIMING dbg_cycles_executed += cpu65_cycle_count; #endif g_dwCyclesThisFrame += cpu65_cycle_count; #ifdef AUDIO_ENABLED MB_UpdateCycles(); // update 6522s (NOTE: do this before updating cycles_count_total) #endif timing_checkpoint_cycles(); #if CPU_TRACING cpu65_trace_checkpoint(); #endif #ifdef AUDIO_ENABLED speaker_flush(); // play audio #endif if (g_dwCyclesThisFrame >= dwClksPerFrame) { g_dwCyclesThisFrame -= dwClksPerFrame; #ifdef AUDIO_ENABLED MB_EndOfVideoFrame(); #endif } clock_gettime(CLOCK_MONOTONIC, &tj); pthread_mutex_unlock(&interface_mutex); // -UNLOCK--------------------------------------------------------------------------------------- SAMPLE tj #if !MOBILE_DEVICE if (timing_shouldAutoAdjustSpeed()) { disk_motor_time = timespec_diff(disk6.motor_time, tj, &negative); assert(!negative); if (!is_fullspeed && #ifdef AUDIO_ENABLED !speaker_isActive() && #endif !video_isDirty() && (!disk6.motor_off && (disk_motor_time.tv_sec || disk_motor_time.tv_nsec > DISK_MOTOR_QUIET_NSECS)) ) { TIMING_LOG("auto switching to full speed"); _timing_initialize(CPU_SCALE_FASTEST); } } #endif if (!is_fullspeed) { deltat = timespec_diff(ti, tj, &negative); assert(!negative); long sleepfor = 0; if (!deltat.tv_sec) { sleepfor = EXECUTION_PERIOD_NSECS - drift_adj_nsecs - deltat.tv_nsec; } if (sleepfor <= 0) { // lagging ... static time_t throttle_warning = 0; if (t0.tv_sec - throttle_warning > 0) { TIMING_LOG("not sleeping to catch up ... %ld . %ld", deltat.tv_sec, deltat.tv_nsec); throttle_warning = t0.tv_sec; } } else { deltat.tv_sec = 0; deltat.tv_nsec = sleepfor; TRACE_CPU_BEGIN("sleep"); nanosleep(&deltat, NULL); TRACE_CPU_END(); } #if DEBUG_TIMING // collect timing statistics if (speaker_neg_feedback > cycles_speaker_feedback) { speaker_neg_feedback = cycles_speaker_feedback; } if (speaker_pos_feedback < cycles_speaker_feedback) { speaker_pos_feedback = cycles_speaker_feedback; } dbg_ticks += EXECUTION_PERIOD_NSECS; if ((dbg_ticks % NANOSECONDS_PER_SECOND) == 0) { TIMING_LOG("tick:(%ld.%ld) real:(%ld.%ld) cycles exe: %d ... speaker feedback: %d/%d", t0.tv_sec, t0.tv_nsec, ti.tv_sec, ti.tv_nsec, dbg_cycles_executed, speaker_neg_feedback, speaker_pos_feedback); dbg_cycles_executed = 0; dbg_ticks = 0; speaker_neg_feedback = 0; speaker_pos_feedback = 0; } #endif } #if !MOBILE_DEVICE if (timing_shouldAutoAdjustSpeed()) { if (is_fullspeed && ( #ifdef AUDIO_ENABLED speaker_isActive() || #endif video_isDirty() || (disk6.motor_off && (disk_motor_time.tv_sec || disk_motor_time.tv_nsec > DISK_MOTOR_QUIET_NSECS))) ) { double speed = alt_speed_enabled ? cpu_altscale_factor : cpu_scale_factor; if (speed < CPU_SCALE_FASTEST) { TIMING_LOG("auto switching to configured speed"); _timing_initialize(speed); } } } #endif if (UNLIKELY(emul_reinitialize)) { break; } #ifdef AUDIO_ENABLED if (UNLIKELY(emul_reinitialize_audio)) { break; } #endif if (UNLIKELY(cpu_shutting_down)) { break; } } while (1); if (UNLIKELY(cpu_shutting_down)) { break; } } while (1); #ifdef AUDIO_ENABLED speaker_destroy(); MB_Destroy(); audio_shutdown(); #endif return NULL; }
void *t_1(void *thread_params) { struct sched_param2 dl_params; struct timespec t_next, t_period, t_start, t_stop, ran_for, t_now, t_crit, t_exec; long tid = gettid(); int retval, i; cpu_set_t mask; __u64 crit, run1, runtime, deadline, period; /* * t_1 should go in budget overflow while in critical section */ run1 = 8U * NSEC_PER_MSEC; crit = 12U * NSEC_PER_MSEC; runtime = run1 + crit + (8U * NSEC_PER_MSEC); deadline = 40U * NSEC_PER_MSEC; period = deadline; t_period = nsec_to_timespec(&period); t_crit = nsec_to_timespec(&crit); signal(SIGHUP, sighandler); signal(SIGINT, sighandler); signal(SIGQUIT, sighandler); CPU_ZERO(&mask); CPU_SET(0, &mask); retval = sched_setaffinity(0, sizeof(mask), &mask); if (retval) { fprintf(stderr, "WARNING: could not set task affinity\n"); exit(-1); } memset(&dl_params, 0, sizeof(dl_params)); dl_params.sched_priority = 0; dl_params.sched_runtime = runtime; dl_params.sched_deadline = deadline; dl_params.sched_period = period; ftrace_write(marker_fd, "[thread %ld (t_1)]: setting rt=%llums dl=%llums\n", tid, runtime/NSEC_PER_MSEC, deadline/NSEC_PER_MSEC); retval = sched_setscheduler2(0, SCHED_DEADLINE, &dl_params); if (retval) { fprintf(stderr, "WARNING: could not set SCHED_DEADLINE" " policy!\n"); exit(-1); } clock_gettime(CLOCK_MONOTONIC, &t_next); for (i = 0; i < NRUN; i++) { ftrace_write(marker_fd, "[t_1] run starts\n"); clock_gettime(CLOCK_MONOTONIC, &t_start); ftrace_write(marker_fd, "[t_1] exec for %lluns\n", run1); busywait(run1); ftrace_write(marker_fd, "[t_1] locks mutex\n"); pthread_mutex_lock(&my_mutex); ftrace_write(marker_fd, "[t_1] exec for %lluns\n", crit); busywait(crit); ftrace_write(marker_fd, "[t_1] unlocks mutex\n"); pthread_mutex_unlock(&my_mutex); clock_gettime(CLOCK_MONOTONIC, &t_stop); t_next = timespec_add(&t_next, &t_period); ran_for = timespec_sub(&t_stop, &t_start); printf("[thread %ld]: run %d for %lluus\n", tid, i, timespec_to_usec(&ran_for)); ftrace_write(marker_fd, "[t_1] run ends\n"); clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &t_next, NULL); } retval = sched_setscheduler2(0, SCHED_OTHER, &dl_params); if (retval) { fprintf(stderr, "WARNING: could not set SCHED_OTHER" "policy!\n"); exit(-1); } }
void os_keypress(usbdevice* kb, int scancode, int down){ if(scancode & SCAN_MOUSE){ if(scancode == BTN_WHEELUP){ if(down) postevent_wheel(kb->event, !!(kb->features & FEAT_MOUSEACCEL), 1); return; } else if(scancode == BTN_WHEELDOWN){ if(down) postevent_wheel(kb->event, !!(kb->features & FEAT_MOUSEACCEL), -1); return; } int button = scancode & ~SCAN_MOUSE; // Reverse or collapse left/right buttons if the system preferences say so int mode; if(IOHIDGetMouseButtonMode(kb->event, &mode) == kIOReturnSuccess){ if(mode == kIOHIDButtonMode_ReverseLeftRightClicks && button == 0) button = 1; else if(mode != kIOHIDButtonMode_EnableRightClick && button == 1) button = 0; } postevent_mb(kb->event, button, down); if(down) kb->mousestate |= (1 << button); else kb->mousestate &= ~(1 << button); return; } // Some boneheaded Apple engineers decided to reverse kVK_ANSI_Grave and kVK_ISO_Section on the 105-key layouts... if(!HAS_ANY_FEATURE(kb, FEAT_LMASK)){ // If the layout hasn't been set yet, it can be auto-detected from certain keys if(scancode == KEY_BACKSLASH_ISO || scancode == KEY_102ND) kb->features |= FEAT_ISO; else if(scancode == KEY_BACKSLASH) kb->features |= FEAT_ANSI; } if(scancode == KEY_BACKSLASH_ISO) scancode = KEY_BACKSLASH; if(HAS_FEATURES(kb, FEAT_ISO)){ // Compensate for key reversal if(scancode == KEY_GRAVE) scancode = KEY_102ND; else if(scancode == KEY_102ND) scancode = KEY_GRAVE; } // Check for modifier keys and update flags int isMod = 0; IOOptionBits mod = 0; if(scancode == KEY_CAPSLOCK){ if(down) kb->modifiers ^= NX_ALPHASHIFTMASK; isMod = 1; } else if(scancode == KEY_LEFTSHIFT) mod = NX_DEVICELSHIFTKEYMASK; else if(scancode == KEY_RIGHTSHIFT) mod = NX_DEVICERSHIFTKEYMASK; else if(scancode == KEY_LEFTCTRL) mod = NX_DEVICELCTLKEYMASK; else if(scancode == KEY_RIGHTCTRL) mod = NX_DEVICERCTLKEYMASK; else if(scancode == KEY_LEFTMETA) mod = NX_DEVICELCMDKEYMASK; else if(scancode == KEY_RIGHTMETA) mod = NX_DEVICERCMDKEYMASK; else if(scancode == KEY_LEFTALT) mod = NX_DEVICELALTKEYMASK; else if(scancode == KEY_RIGHTALT) mod = NX_DEVICERALTKEYMASK; if(mod){ // Update global modifiers if(down) mod |= kb->modifiers; else mod = kb->modifiers & ~mod; if((mod & NX_DEVICELSHIFTKEYMASK) || (mod & NX_DEVICERSHIFTKEYMASK)) mod |= NX_SHIFTMASK; else mod &= ~NX_SHIFTMASK; if((mod & NX_DEVICELCTLKEYMASK) || (mod & NX_DEVICERCTLKEYMASK)) mod |= NX_CONTROLMASK; else mod &= ~NX_CONTROLMASK; if((mod & NX_DEVICELCMDKEYMASK) || (mod & NX_DEVICERCMDKEYMASK)) mod |= NX_COMMANDMASK; else mod &= ~NX_COMMANDMASK; if((mod & NX_DEVICELALTKEYMASK) || (mod & NX_DEVICERALTKEYMASK)) mod |= NX_ALTERNATEMASK; else mod &= ~NX_ALTERNATEMASK; kb->modifiers = mod; kb->lastkeypress = KEY_NONE; isMod = 1; } else if(!isMod){ // For any other key, trigger key repeat if(down){ long repeat = repeattime(kb->event, 1); if(repeat > 0){ kb->lastkeypress = scancode; clock_gettime(CLOCK_MONOTONIC, &kb->keyrepeat); timespec_add(&kb->keyrepeat, repeat); } else kb->lastkeypress = KEY_NONE; } else kb->lastkeypress = KEY_NONE; } postevent_kp(kb->event, kb->modifiers, scancode, down, isMod, 0); }