This one is a little unintuitive to think about with Onyx windows because our state API is capable of handling events received out-of-order. That is, we can correctly handle data send with events whose timestamps are received in an order in which they did not occur.
Given that windows anticipate receiving data from the (potentially very long ago) past, "timing out" a window isn't something that's support directly from the API. Using the 15 minute fixed windows approach doesn't work well because we assume each 15 minute window could receive data from any timestamp at any point in the stream. Session windows are closer to what you want, but they don't have a notion of timing out due to inactivity either. They only expand.
I would recommend Session windows (with session key user ID), and a timer trigger. The trick here is that each time the trigger fires, you need to compare "this state" with the state from the previous trigger. We currently don't keep the previous state around to do a diff, but I'm planning on adding a patch for this soon since I've encountered the same problem. For the moment, you can place an atom in the event map and swap the state into it's contents. This basically let's you keep around the previous state to do the comparison.
If you have both the current state and the previous state, you can determine which users haven't had any activity based off the wallclock time of the machine. This has the obvious downside of requiring that the wallclock of the peer is close enough to being correct, but this problem inherently involves wallclock time since all data is presumed to be received in order.
Happy to elaborate more if needed.