The answer to this question has absolutely nothing to do with OpenGL or DirectX. It is entirely dependent on how you implement your event loop in your window system code.
The OS's windowing system API is built around the concept of an event loop. Whenever the user does something, like press a key or move the mouse, an event is sent from the windowing system to the application.
Typical applications, including 3DS Max, use an event loop something like this pseudo code:
while (WaitForMessage())
{
ProcessMessage();
UpdateState();
}
If there are no messages from the windowing system (because the user is not interacting with the application), then WaitForMessage will block (put the application to sleep) until a new message arrives. Hence the application is not using any CPU/GPU Time.
Typical games are full of animations and game logic that is happening whether the player is actively pressing keys or moving the mouse. It would be pretty silly if the AI-controlled enemies only moved while the user was moving the mouse. They use an event loop something like this pseudo code:
while (IsPlaying())
{
if (PeekMessage())
ProcessMessage();
UpdateState();
}
Hence they are constantly running in a loop as fast as possible. This event loop is mandatory, so even games that are doing all input by polling the OS must still have that event loop, since applications are required to respond to and process messages from the windowing system. Otherwise the system will consider your app hung/nonresponsive, and might kill it. Also, there are other special system messages an app must handle that aren't related to user interaction.
That loop is somewhere in the app. If you're using something like GLUT or certain other toolkits, the loop might be hidden away from you. Those toolkits are generally not meant for games or "serious" apps, and I recommend avoiding them and either using native code (e.g. the Win32 API on Windows, X11 on UNIX/Linux systems, etc.) or using more capable wrapper library (SDL being a good example of one).
Note that this is a bit more complex with actual graphics code, since you can use the "sync to v-blank" option to only render as fast as the monitor can display new images. A game using this can still easily achieve well under 100% system utilization, assuming it can update and draw an entire frame much quicker than the monitor's native frame time. More complex 3D apps can struggle to maintain the native 60Hz (for typical monitors) and so will continue to consume near 100% of the processing time.
Things get a bit more complex when you want to have animations in your app, but don't want it to waste resources. Typically, normal desktop applications will use a built-in timer system in the windowing system. These timers will send messages to the application when they fire. So if you have an animation that needs to play at 15 frames per second, you would create a timer in the windowing system set to fire every 1/15th of a second. That first message loop would then receive a timer message every 1/15th of a second, allowing you to update the animation state. When the animation is complete, you would disable the timer so that your application doesn't keep receiving unnecessary timer messages (and hence it can sleep at 0% CPU utilization until the user interacts with it again).
This timer approach can be a little bit hairier. If you have multiple animations, you don't want to create multiple timers in the windowing system; in fact, some windowing systems only allow a single timer or a small number of timers. You'll want to have your own animation manager in your application that can be driven by a single timer message, and which can easily know if there are no pending animations and hence that the timer is unnecessary.
For games, the timer approach is generally not suitable. Windowing system timers are driven by the OS's low-precision timer features, generally, which work well enough for low-framerate animations (like most UI animations) but are not precise enough for the framerates that games want to run at (30 at a minimum, 60+ ideally). This is why games use the loop above.
If you want to make your apps behave like 3DS Max, just use the first kind of event loop.
If you want to make a game that doesn't use 100% CPU/GPU time unnecessarily, do two things. First, use sync to v-blank if your game is capable of rendering a consistent 30+ FPS. That way your app will only update as fast as is necessary. Second, when your game is in a state where there is no animation or AI updates or networking to deal with (say, in the pause screen in a single-player game), simply switch your update loop. You can do it with something like this pseudo code:
while (IsPlaying())
{
if (IsPaused() && WaitForMessage())
ProcessMessage();
else if (IsNotPaused() && PeekMessage())
ProcessMessage();
UpdateState();
}
Be careful with things like your time delta with code like this. When you come out of the pause state your time delta will be quite large since UpdateState() was not called at all during the pause state. You may want to skip doing any time-dependent updates on the first frame after unpausing, or simply cap your time delta at some reasonable time (1/15th of a second or so). And of course, always fix your timestep!