[HN Gopher] Show HN: X11 tool to share a screen area in any vide...
___________________________________________________________________
Show HN: X11 tool to share a screen area in any video meeting
Author : splitbrain
Score : 205 points
Date : 2024-10-14 13:11 UTC (9 hours ago)
(HTM) web link (github.com)
(TXT) w3m dump (github.com)
| splitbrain wrote:
| I have a big 49" wide screen monitor and sharing my screen in
| Google Meet was cumbersome because you can only share a window or
| the whole screen, but not a screen region.
|
| So I wrote a small tool that uses the xrandr extension to mirror
| an area to a virtual monitor which then can be shared.
|
| See my blog post for some more details:
| https://www.splitbrain.org/blog/2024-10/11-introducing_clips...
| z991 wrote:
| Wow, this is fantastic! This exact use case, on Linux, is why our
| company selected Zoom instead of Meet.
|
| Awesome!
| z991 wrote:
| Built it and took a fullscreen screenshot with GIMP to figure
| out the width/height/x/y coordinates I wanted and tested with
| Google Meet. Working perfectly!
| machinestops wrote:
| https://github.com/naelstrof/slop Can also use a utility like
| this one, which lets you select an area of the screen and
| output it in a specified format.
| z991 wrote:
| Wow that is also very cool. For those wondering, this is
| what it looks like: $ sudo apt install slop
| $ slop <selects an area on screen>
| 1719x1403+1080+277
| machinestops wrote:
| Putting the two together is easy too:
|
| $ clipscreen $(slop -F "%x %y %w %h")
|
| NB. The lack of quotes around $() enables wordsplitting
| to occur.
| samwhiteUK wrote:
| Or
|
| $ clipscreen $(slop | tr -s "+x" " ")
| Narishma wrote:
| I think you got the size and position switched.
| hackernewds wrote:
| this is just cmd+shift+5 in a Mac OS
| yjftsjthsd-h wrote:
| Is it? I thought that took a screenshot, not fed
| coordinates to a program (in this case a screen sharing
| program)
| hackernewds wrote:
| Did you have any issues implementing?
| amelius wrote:
| Nice. This is the first time I read about creating a virtual
| monitor in X.
| OsrsNeedsf2P wrote:
| I love how simple this is- Barely 100 lines or C++ (ignoring
| comments). That's one thing that makes me prefer X11 over
| Wayland.
| ajross wrote:
| Yeah. I mean, not to deny the decades of arguments over its
| warts, but it's kind of amazing to me the extent to which X11
| has emerged as, well, _the simplest /best and most hackable_
| desktop graphics environment available. You want to play a
| trick, it's right there. The ICCCM got a ton of hate back in
| the early 90's, but... no one else has an equivalent and people
| still innovate in the WM space even today.
| WD-42 wrote:
| Hackable is right. But not always in the positive sense of
| the word.
| ajross wrote:
| FWIW, the threat model you're imagining is an attacker
| being able to run code to display directly to the desktop
| using the lowest level native API. A local[1] code exploit
| at the level of an interactive user is _already_ a huge
| failure in the modern world.
|
| Is that a reasonable argument against using X11? Sure, for
| some use cases. Is it a good argument _for_ wayland
| /windows/OSX/whatever to do your tiling WM experimentation?
| Not really, those environments kinda suck for playing
| around with.
|
| [1] Or "local-ish", your system or a trusted remote has to
| have been compromised already. Untrusted X11 protocol still
| exists but is deliberately disabled (and often blocked)
| everywhere. Even ssh won't forward it anymore unless you
| dig out the option and turn it on manually.
| boudin wrote:
| Isn't any app that can access read the x11 socket able to
| read any input? It's not just running an explicitly
| malicious app but also the risk of compromising an app
| which can read the x11 socket (e.g. Firefox)
| ajross wrote:
| Yes, exactly. I'm just saying that the response to a
| remote browser exploit in firefox is more likely to be
| "YIKES ZERO DAY IN FIREFOX!!!!!" and not "well it's a
| good thing we're running it in windows so it can't
| screenshot other apps or inject key events".
|
| It's not like it's not a valid argument, just that it's
| sort of a nitpick. Security is hard, and defense in depth
| is a thing, but this particular attack surface is way,
| way back in the "depth" stack for a modern app
| deployment.
| superkuh wrote:
| Javascript has managed to even ruin the linux desktop.
| Running every random JS application sent to your browser
| VM makes the browser insecure which means the entire
| computer can't be trusted. This is the reason things like
| the waylands enforce a smartphone like model of security
| where the user's applications aren't allowed to
| communicate or interact with other elements of the
| graphical desktop. Applications aren't trusted. So the
| user isn't trusted. A trade-off not worth it.
| quotemstr wrote:
| Huh? What are you trying to say? There's no conflict
| between distrusting applications and trusting the user.
| Even on Android (which is pretty paranoid these days),
| you, the user, can still opt to trust apps with things
| like accessibility API access and background location.
|
| Why exactly should we perpetuate the insecure old single-
| privilege-level desktop model?
| p_l wrote:
| It's also why there existed more advanced security
| extensions for X11 (like security labels for windows),
| but also why even bare-bones X11 had methods to ensure
| that only one specific application was getting input,
| specifically to handle secure input like with passwords.
| l72 wrote:
| I find it very interesting how much our threat model has
| changed in the last 10-15 years. We no longer trust even
| local software, as we have to assume everything is now
| malicious. Commercial software from "reputable" companies
| can't be trusted to not pull a ton of analytics and
| personal data off your computer. We now have to worry about
| every piece of software being a keylogger and spying on
| other windows/applications and reporting back.
|
| We've had to give up so much flexibility. Wayland certainly
| focuses on plugging this hole, but it means we've lost all
| these cool utilities like this one. There was just so much
| you could do with devilspie, xdotool, and others to make
| sure my operating system and window environment worked for
| me.
|
| I still really miss X11's Zaphod mode, where you had two
| independent X sessions (:0.0 and :0.1) on two different
| monitors, with different window managers and different
| windowing rules.
|
| I miss the days of being able to trust my computer and
| trust my software.
| singpolyma3 wrote:
| If you can't trust your locally installed software,
| everything is lost. I understand where this new threat
| model comes from for some people but I'd rather continue
| to avoid bad software sources than hamstring my OS in the
| hopes of avoiding malware I installed on purpose.
| l72 wrote:
| I agree. But can you trust Zoom? What about Office or
| Photoshop? Can you trust Websites or your browser
| anymore? Even open source apps have analytics in them
| that may not be trustworthy anymore (firefox, audacity,
| ...).
| singpolyma3 wrote:
| This is why I don't run Zoom or Office or Photoshop or
| versions of Firefox or Audacity not distributed by
| Debian.
|
| Browser sandboxes pretty heavily though of course one
| does want to be a bit careful there too.
| jrm4 wrote:
| I teach online for a living, and -- yep Zoom through
| FIREFOX only.
|
| Coincidentally, it's also the best experience, for
| whatever reason it's the only on that supports virtual
| backgrounds on Linux for me? Neither Chrome nor Desktop
| seem to work for this.
| marcosdumay wrote:
| > If you can't trust your locally installed software,
| everything is lost.
|
| That's only true if you decide to trust it.
|
| You can deal perfectly well with software you distrust,
| and not have it harm your system.
| singpolyma3 wrote:
| Yes. By not running it.
| themerone wrote:
| X11 is the opposite of simple and hackable. What you are
| thinking of as "hackable" is actually the result of it having
| a ton of legacy features that enable users to do neat tricks.
|
| Wayland breaks a lot of these tools because it is so much
| simpler than X.
| ajross wrote:
| Lacking features isn't the same thing as "simpler", Wayland
| is great, but is very much a subset of the features
| implemented on an X11 desktop. Wayland doesn't do
| selections or provide any IPC mechanism of its own, much
| less something like an ICCCM that allows you to
| identify/target other users of the desktop and interact
| with them in a flexible way. In fact as I understand it the
| linked tool is in fact impossible to write in Wayland.
|
| Again, this isn't the fault of "Wayland", which is just a
| compositor framework. The complaint is that the ecosystem
| of "desktop" software which evolved around Wayland is an ad
| hoc monstrosity that lacks the unified structure that its
| ancestor had way back in the X11R5 days.
| vidarh wrote:
| By window manager started out as ~50 lines of Ruby copying
| an equivalent amount of C.
|
| You can say many things about Wayland, but it's "simple"
| from a point of view I for one really do not care about.
| Wayland may be "simple" in some respects, but it makes most
| of the things I care about doing unnecessarily complex.
| bee_rider wrote:
| Walyand probably would have been better if wlroots had
| been developed as a (whatever this means) first-party
| "built-in" library.
| anthk wrote:
| The most hackable would have been a Lisp based desktop.
| sim7c00 wrote:
| there's very little code because there's very little error
| handling / sanity checking. not saying X11 isn't hackable and
| cool, but a lot of code gets bloated and complex (and robust!)
| by not assuming perfect usage.
|
| for example. run ./clipscreen 1 2 3 4
| splitbrain wrote:
| True. If something goes wrong this will just crash. But to be
| fair, the only error handling I could think of would probably
| just exit with a vague error message... Pull requests to make
| it more robust welcome anyway!
| xrd wrote:
| To the parent, splitbrain just got you to QA this for him.
| The true cost of software is the maintenance and QA, and he
| got you to do free work, and here I am doing free work
| writing about it. How hard we BOTH just got pwned! </joke>
| sim7c00 wrote:
| will work for food
| sim7c00 wrote:
| haha yeah, its ok for a tool its really cool honestly :p
| just commenting on the 'so little code' might be good to
| check if the x y etc. are within the screen / set
| resolution perhaps.
| asveikau wrote:
| The code is a little weird. There is no XLib event loop. It
| calls sleep(100) in a loop until it hits SIGINT. That will have
| high cpu usage for no reason.
| diath wrote:
| It will not, even adding just a 1ms sleep in a loop will drop
| CPU usage to barely noticeable levels, 10 wakes a second is
| barely anything for any CPU from the past 3 decades.
| asveikau wrote:
| Not my experience at all. Granted I haven't tried writing a
| loop like this in 20ish years, because once you spot that
| mistake you don't tend to make it again, and CPUs are
| better now.
|
| Another thing to note is when you call sleep with a low
| value it may decide not to sleep at all, so this loop just
| might be constantly doing syscalls in a tight loop.
| diath wrote:
| > Not my experience at all. Granted I haven't tried
| writing a loop like this in 20ish years, because once you
| spot that mistake you don't tend to make it again, and
| CPUs are better now.
|
| You can trivially verify it by running the following, I
| have personally been using "sleep for 1ms in a loop to
| prevent CPU burn" for years and never noticed it having
| any impact, it's not until I go into microseconds when I
| can start noticing my CPU doing more busy work.
| // g++ -std=c++20 -osleep sleep.cpp #include
| <thread> #include <chrono> int
| main(int, char **) { while (true) {
| std::this_thread::sleep_for(std::chrono::milliseconds
| {1}); } return 0; }
|
| > Another thing to note is when you call sleep with a low
| value it may decide not to sleep at all, so this loop
| just might be constantly doing syscalls in a tight loop.
|
| On what system? AFAIK, if your sleep time is low enough,
| it will round up to whatever is the OS clock resolution
| multiple, not skip the sleep call completely. On Linux,
| it will use nanosleep(2) and I cannot see any mention of
| the sleep not suspending the thread at all with low
| values.
| asveikau wrote:
| If memory serves, Windows treats a sleep under the
| scheduler quantum length as a yield. It may take you off
| the cpu if there's something else to run but it may not.
| Meanwhile burning up cycles may prevent low power states.
|
| At any rate, back to the code at hand, there are many
| ways to block on SIGINT without polling. But it's also
| hugely odd that this code does not read events from the
| X11 socket while it does so. This is code smell, and a
| poorly behaved X client.
| orbisvicis wrote:
| I thought that Linux behaved the same, but I'm not
| finding any proof in `man 2 nanosleep`...
| eqvinox wrote:
| You can't find that proof because Linux does the
| opposite. Unless your task is SCHED_REALTIME, all timers
| have a little bit of slack at the end that allows the
| kernel to group wakeup events. You can configure this
| (for non-RT tasks) with prctl(PR_SET_TIMERSLACK).
|
| https://lxr.linux.no/#linux+v6.7.1/kernel/time/hrtimer.c#
| L20...
|
| https://www.man7.org/linux/man-
| pages/man2/PR_SET_TIMERSLACK....
| thwarted wrote:
| This is what the pause(2) syscall was made for, waiting for
| a signal forever.
| Too wrote:
| It's a good way to drain your battery on mobile devices,
| even if usage looks low.
|
| Not that this matters for this particular tool.
| erickj wrote:
| > Not that this matters for this particular tool.
|
| Then the code is perfectly appropriate.
| quotemstr wrote:
| It's a bad example for others and a bad habit to get
| into. If every program did this, we'd have trouble
| getting CPUs into deep idle states.
| enriquto wrote:
| It's an irrelevant implementation detail. This is for a
| live call. You are streaming video at the same time, so
| there's no point in worrying about idling.
|
| I'd even say that it's a _good_ example for others,
| because the equivalent code with the event loop would be
| slightly more complicated (maybe 5 more lines?). Striving
| for "doing things right" when the wrong thing is
| perfectly appropriate would be a bad example.
| asveikau wrote:
| My guess is that somebody coded that event-loop-less X
| client not really familiar with the language and how to
| write Xlib apps. I partially assume this because C, C++
| and especially Xlib are becoming less popular over time,
| so finding skilled practitioners to write it
| idiomatically is relatively rare now. This basic event
| loop stuff is something that maybe belongs in a library.
| So they just wrote library grade functionality
| themselves, badly. The commentary here is getting
| defensive about doing things the wrong way, coming up
| with lots of post hoc justification.
| jchw wrote:
| This certainly is an elegant X.org party trick that can't be
| done easily in almost any other windowing system: creating a
| virtual Xrandr display that overlaps with existing physical
| displays. It's slightly awkward since if it exits outside of
| sigint it will leave a virtual output and no overlay window but
| that's a pretty minor issue. (All of that having been said, I
| would strongly advise to not over-index on SLoC as a measure of
| quality or elegance.)
|
| This flat-out can't be done in Wayland. Though all is not lost,
| you might not need this at all in Wayland. The standard way to
| capture the screen from an unprivileged process in Wayland is
| through desktop portals, and at least KDE supports a wide
| variety of different capture options including capturing a
| rectangle of the screen. I haven't tried, but I suspect this is
| even true when running X.org applications, thanks to
| XWaylandVideoBridge.
|
| I am not really thrilled about D-Bus stuff everywhere, but it
| is nice that you can pretty much override any screen capture
| behavior you want by changing the
| org.freedesktop.impl.portal.ScreenCast implementation: I think
| that's actually a step in a better direction versus having
| every application implement its own functionality for selecting
| capture targets.
| rnhmjoj wrote:
| To me it's quite sad that for a lot of things, the "standard"
| way of doing something is not actually part of the standard
| (XDG portals, third party protocols, etc.). Yes, X.org is
| old, bloated, unmaintainable and whathever, but at least
| every desktop environment used the same X server
| implementation and the same tools worked everywhere.
|
| Besides the duplication of efforts in implementing the same
| stuff over and over, now someone developing somewhat non-
| trivial programs needs to be aware of the differences in
| supported features and non-standard extensions in all
| desktops, for example [1].
|
| [1]: https://wayland.app/protocols/cursor-
| shape-v1#compositor-sup...
| jchw wrote:
| I think Wayland had made some mistakes, no doubt. Cursor
| shape just should've been part of the protocol. Wayland has
| its fair share of misfires.
|
| That said, I understand what they were going for. They
| really wanted to make the compositor as small and simple as
| possible, so for example you would just use libwayland-
| cursor instead of bothering with cursors yourself. However
| there are a lot of ways libwayland-cursor worked out
| poorly... Not everyone agreed on how scaling should work,
| GTK4 ditched it for performance reasons, and overall it's
| just inconvenient for a lot of cases (languages other than
| C, sandboxing, etc.) And to make matters even worse, in
| practice every compositor needed to load and handle XCursor
| themes anyways...
|
| That said, I think that it's okay if Wayland doesn't own
| the majority of the Linux desktop stack the way X11 did.
| It's fine for compositors and their helpers to implement
| protocols from other projects, too. That way Wayland can be
| more applicable to graphical machines other than desktops
| without bringing unnecessary baggage. It'll always have
| trade-offs, of course, but I think it's far from the end of
| the world.
| tapoxi wrote:
| In Wayland you just start a capture with the xdg-desktop-portal
| API and it notifies the user and let them select the area to
| capture.
| gchamonlive wrote:
| Yes, but I believe op was refering to how interacting with
| all things Wayland seems to be more involved than with x11.
| I'm not sure this is indeed like this, I have zero experience
| in developing for Wayland, but I think this is what op meant.
| tapoxi wrote:
| From a quick "how do I implement this in Python" with
| ChatGPT it seems to be about 30 lines, since most of the
| heavy lifting is done for you by the API.
| Zetaphor wrote:
| As someone who uses LLM's regularly to assist in code
| creation, take that output with a huge grain of salt
| until you've actually tested it. Especially as it relates
| to Wayland, I've pulled my hair out trying to get an LLM
| to assist with very similar tasks to this.
| p_l wrote:
| It means you got to tickle the banana, good luck making
| sure that the gorilla holding it is fine with that.
| yndoendo wrote:
| Wayland is more focused on security. That onion layer right
| there will increase the complexity of usage. X11 doesn't
| have the extra abstractions to limit and prevent intrusive
| interactions with the desktop.
|
| Example of this would be where "runas /user:smith
| application.exe" is simple but does not work when a Windows
| Service is required to run an application as the user
| signed in. One must use Window's API to pull in the
| account's token and use more API to execute
| "application.exe". UltraVNC is a great source to see all
| the extras needed.
| teekert wrote:
| Is it much more difficult under Wayland?
| favorited wrote:
| Wayland intentionally makes this more difficult, because one
| of the security goals of the project is that (by default)
| Wayland clients shouldn't have visibility into other clients'
| window contents/events/etc.
|
| Of course, it still needs to be _possible_ under Wayland,
| because there are plenty of legitimate use-cases
| (screenshots, screen sharing, video capture, etc.), but it
| was a non-goal to make it as simple as X.
|
| Wayland merged the image-capture-source and image-copy-
| capture protocol extensions earlier this year:
| https://www.phoronix.com/news/Wayland-Merges-Screen-Capture
| enriquto wrote:
| > Wayland intentionally makes this more difficult,
|
| some men just want to watch the world burn
| Brajeshwar wrote:
| Also, I remember a friend showing me in Zoom that you can share
| not just one but multiple screens/windows--press the SHFT key
| while clicking the windows you want to share.
| HPsquared wrote:
| How do people discover these things?
| Brajeshwar wrote:
| The same question I asked (that was me after using Zoom for
| 3+ years).
| jdbdndj wrote:
| Isn't that the same of how you select multiple files in most
| file managers?
|
| Shift+Click: select from currently selected item to clicked
| item
|
| Ctrl+Click: add/remove clicked item to set of selected items
| hackernewds wrote:
| Sure, but the idea you can share multiple windows this way.
|
| Can Google Meet (or hangout or w/e they call it now adays)
| do some of this?
| TacticalCoder wrote:
| That s very cool... Speaking of which: any easy way to allow two
| people, both on X, to both share and interact (keyboard and
| mouse) with a common X window?
|
| The app that we d like to share and both control is a browser
| (running on a machine on our LAN) so a browser extension would
| work too I guess.
| patrakov wrote:
| My preferred solution for that would be a VNC server (so that
| it shares the whole screen) installed in a VM.
| bee_rider wrote:
| I think there was some way to do that with existing tools. I
| forget the details because I only threw it together as a bit of
| fun novelty. I think the terms to google are x2x and multiseat
| though, at least to start your search...
| benjiweber wrote:
| This is brilliant. I've wanted this so many times and had to
| awkwardly switch between window being shared instead.
| fweimer wrote:
| I wouldn't mind switching between windows if I could use the
| GNOME Activities overview for that. But maybe that is not
| possible because there is no way to communicate the change in
| stream size if the windows have different sizes?
| snowe2010 wrote:
| Dang. I need this for Mac. I've been wishing I had exactly this
| for years.
| thefreeman wrote:
| I use "Advanced Screen Share" for this purpose. it has a one
| time purchase if you want to remove a small overlay but it gets
| the job done and is installable through the app store.
| _joel wrote:
| Wasn't the same thing posted for MacOS a few days back, can't
| recall the name? Looking at the time on the repo makes me think
| the author pushed after seeing people requesting something
| similar for Linux.
|
| edit: Here you go https://github.com/Stengo/DeskPad
| joombaga wrote:
| That's not quite the same. With DeskPad you have to move the
| window to the virtual monitor. clipscreen allows you to
| select a portion of your screen without moving any windows.
| iknowstuff wrote:
| you can just share multiple windows on the newest macOS and
| they will ne nicely arranged for viewers. You can even add the
| presenter thing to show your face next to them.
| ho_schi wrote:
| Neat. Now I want for Wayland. Don't use X11 for some years.
| singpolyma3 wrote:
| Never to late to upgrade to X11 :)
| udev4096 wrote:
| This is only helpful if you are using a desktop environment. What
| about window managers like i3?
| hamdouni wrote:
| I'm not sure about i3 but you can have floating windows in DWM
| and move them to the targeted area with the mouse
| salviati wrote:
| Do I understand correctly that you could to this with OBS on any
| platform, including Wayland? I'm reading many comments that make
| me think either many people don't know about OBS, or I'm
| overestimating it's abilities.
| splitbrain wrote:
| You probably can. I never used OBS, but it's probably a bit
| more than a 20kb binary though ;-)
| lopkeny12ko wrote:
| I don't understand, what is the significance of a 20kb
| binary? The only person using this would be someone who takes
| Zoom meetings on a company-issued computer and I can't
| imagine such machines are disk space-constrained.
| hamdouni wrote:
| I'm not aware of company issued computer with x11. Is it
| really a thing ?
| phkahler wrote:
| Some companies let you run Linux on their company issued
| computer.
| phkahler wrote:
| OBS lets you share a window or just the client area of an app.
| movedx wrote:
| With OBS, you can add an entire screen to your canvas and
| then add a filter to crop it down to a particular part of
| that screen. This nets you the same results as the small C++
| tool being proposed here.
|
| A lot more work involved, though.
| movedx wrote:
| It can do that, yes, but it's a bit more work. There are
| several GUI hoops you'll have to run through to get that to
| work, and if you have to adjust it each and every time, before
| a meeting, then it would become burdensome. But yes, it can be
| done.
| procparam wrote:
| I've always wanted something like this, but for i3 workspaces.
| Something like "share workspace 2." Anyone know how to accomplish
| this?
| alanjames00 wrote:
| I've looking for something like this for quite sometime. It's
| simple, clean and elegant.
| tcsenpai wrote:
| This is surely useful right now. I wonder what will happens to
| all the nice X11 tools once Wayland (hopefully soon) will be the
| golden standard. There are options to enable X11 behaviors in
| Wayland but I guess that is just a fallback to the insecure
| implementation.
| yazzku wrote:
| Can you not use std::condition_variable to avoid the active
| waiting of the signal?
| quotemstr wrote:
| signalfd or ppoll or a million other options
| listeria wrote:
| I don't know if std::condition_variable is async-signal safe,
| but an easy fix is to replace the sleeping with pause(2).
| There's also sigwait(3), which wouldn't need a signal handler.
| IceDane wrote:
| You can literally do this with just xrandr.
|
| xrandr --setmonitor screenshare 2560/1x1440/1+0+0 none
| attah_ wrote:
| In fairness; that and the overlay _is_ what is happening, just
| from C++. Props for nice oneliner none the less. :)
| 0cf8612b2e1e wrote:
| Can someone explain why this is still an unmet need within the
| current video conference platforms? Giant monitors have become
| increasingly common-especially for the developers who might be
| working on these tools.
| simonmysun wrote:
| Maybe because a workaround with OBS isn't that difficult?
| movedx wrote:
| You might have missed the point being made here.
|
| We, as engineers, can't expect everyone to know what OBS is,
| download it, learn in, and use it every day to enable their
| ability to share a sub-section of their monitor (regardless
| of its size.)
|
| We, as engineers, _are_ expected to make our software easier
| to use and feature rich. Adding this capability into Zoom or
| Meet, etc. is a reasonable thing to expect from a software
| company of note. And people _do_ know what Zoom and Meet are,
| download them, learn them, and use them every day. Why not
| implement the feature directly into the software they're
| already using?
| shmerl wrote:
| I'm waiting for ffmpeg to implement pipewire screen grab so it
| could work on Wayland.
| attah_ wrote:
| I was just about to go looking for something like this! I'll look
| so pro on the meeting tomorrow :)
___________________________________________________________________
(page generated 2024-10-14 23:01 UTC)