diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..6c1ca06 --- /dev/null +++ b/.clang-format @@ -0,0 +1,178 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveMacros: None +AlignConsecutiveAssignments: None +AlignConsecutiveBitFields: None +AlignConsecutiveDeclarations: None +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortEnumsOnASingleLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: true +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 100 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseLabels: false +IndentCaseBlocks: false +IndentGotoLabels: true +IndentPPDirectives: None +IndentExternBlock: AfterExternBlock +IndentRequires: false +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertTrailingCommas: None +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +LambdaBodyIndentation: Signature +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PenaltyIndentedWhitespace: 0 +PointerAlignment: Left +PPIndentWidth: -1 +ReferenceAlignment: Pointer +ReflowComments: true +ShortNamespaceLines: 1 +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceAroundPointerQualifiers: Default +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +BitFieldColonSpacing: Both +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseCRLF: false +UseTab: Never +WhitespaceSensitiveMacros: + - STRINGIZE + - PP_STRINGIZE + - BOOST_PP_STRINGIZE + - NS_SWIFT_NAME + - CF_SWIFT_NAME +... + diff --git a/Makefile b/Makefile index 92f8917..e114f3f 100644 --- a/Makefile +++ b/Makefile @@ -12,3 +12,6 @@ build/xrandr: build/Makefile src/xrandr.cpp CMakeLists.txt clean: rm -fr build + +run: build/xrandr + ./build/xrandr diff --git a/README.md b/README.md index 73508cd..8ead3da 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,10 @@ obviously X Windows. In progress. -**DETAILS:** +DETAILS: + +**Notes:** + **BUILDING:** diff --git a/docs/Progress.md b/docs/Progress.md new file mode 100644 index 0000000..1395462 --- /dev/null +++ b/docs/Progress.md @@ -0,0 +1,40 @@ +Resources used in the production of this program: + +- [The XCB Reference from FreeDesktop](https://xcb.freedesktop.org/manual/index.html) +- [The Xorg Basic Programming with the XCB + Library](https://www.x.org/releases/X11R7.6/doc/libxcb/tutorial/index.html), +- [The KDE Source Code for + XCBWrapper](https://github.com/KDE/libkscreen/tree/master/backends), +- [xedgewarp](https://github.com/Airblader/xedgewarp) + [Vlad Zahorodnii](https://github.com/zzag) et. al., ongoing. +- [C++ in a + Nutshell](https://www.oreilly.com/library/view/c-in-a/059600298X/) + Ray Lischner, O'Reilly Publishing, 2003 + +The sheer variety of sources I needed to cobble together an +understanding how the XCB library works is an example of what teachers +call [extrinsic +load](https://betterhumans.pub/skyrocket-your-learning-top-3-studying-techniques-based-on-cognitive-load-theory-1641b5e56508), +an unnecessary extra load on learning because the documentation is +poorly organized and lacks instruction. *Basic Programming with XCB* +is a +*[tutorial](https://www.writethedocs.org/videos/eu/2017/the-four-kinds-of-documentation-and-why-you-need-to-understand-what-they-are-daniele-procida/)* +oriented toward commonplace tasks, but peters out before getting to an +XCB extensions such as RandR. The reference is a *reference*; it +doesn't help you understand how to use the library. `XCBWrapper` is +an excellent example of how to use XCB, but it uses some fairly +high-level C++ to accomplish all that it does, and untangling the +relationship between the wrapper template, the macro that does wrapper +declarations, and the XCB Reference; once you find the insight that +XCB's declarations are all derived from a massive XML file, you can +start to understand that XCBWrapper exploits the patterns produced by +the derivative file, but it requires insight and effort that's +unrelated to understanding XCB in the first place. + +This project does show the usual trajectory of one of my learning +exercises, especially since I'm fond of delving in places where no man +has documented before. I've dumped a ton of stuff into my brain and +now it's all starting to make sense. I also note that I'm doing +_better_ than a lot of the open-source examples, in that I'm batching +many of my requests before processing them. I'm not batching storing +the replies yet, but I don't see why that couldn't happen. diff --git a/notes.md b/notes.md new file mode 100644 index 0000000..9ae2e6f --- /dev/null +++ b/notes.md @@ -0,0 +1,8 @@ +- A Display has Screens +- A Screen has Monitors & Windows +- The root Screen is accessed by traversing to the head of the list of + screens using an interator. +- The root screen's root window is used as the parameter for + 'get_screen_resources'. +- The screen_resources object is contains 'screen_resources_crtcs', + which you again get via an iterator. These are cookies. diff --git a/src/xrandr.cpp b/src/xrandr.cpp index 5aacce9..89a978a 100644 --- a/src/xrandr.cpp +++ b/src/xrandr.cpp @@ -1,14 +1,154 @@ +#include "xcb/randr.h" +#include "xcb/xcb.h" +#include "xcb/xcb_aux.h" #include -#include +#include +#include -int main(int argc, const char *argv[]) { - xcb_connection_t *xConnection = xcb_connect(nullptr, nullptr); - for (auto iter = xcb_setup_roots_iterator(xcb_get_setup(xConnection)); - iter.rem; xcb_screen_next(&iter)) { - xcb_screen_t *screen = iter.data; - std::cout << "Screen " << iter.index << " (" << screen->width_in_pixels - << ", " << screen->height_in_pixels << ")" << std::endl; - } - xcb_disconnect(xConnection); - return 0; +xcb_randr_get_screen_resources_reply_t* get_screen_resources(xcb_connection_t* connection, + xcb_window_t rootWindow) { + return xcb_randr_get_screen_resources_reply( + connection, xcb_randr_get_screen_resources(connection, rootWindow), nullptr); +} + +std::vector get_crtc_cookies(xcb_randr_get_screen_resources_reply_t* resources) { + std::vector reply; + xcb_randr_crtc_t* crtcs = xcb_randr_get_screen_resources_crtcs(resources); + const int crtcsCount = xcb_randr_get_screen_resources_crtcs_length(resources); + for (int i = 0; i < crtcsCount; ++i) { + reply.push_back(crtcs[i]); + } + return reply; +} + +void display_one_crtc(xcb_randr_get_crtc_info_reply_t* crtc) { + std::cout << "(x: " << crtc->x << ", y: " << crtc->y << ") (width: " << crtc->width + << ", height: " << crtc->height << ") status:" << unsigned(crtc->status) << std::endl; +} + +void display_crtc_info(xcb_connection_t* connection, std::vector& crtc_cs) { + std::vector cookies; + xcb_generic_error_t* error = nullptr; + for (const auto& crtc : crtc_cs) { + cookies.push_back(xcb_randr_get_crtc_info(connection, crtc, XCB_CURRENT_TIME)); + } + for (const auto& cookie : cookies) { + xcb_randr_get_crtc_info_reply_t* reply = + xcb_randr_get_crtc_info_reply(connection, cookie, &error); + if (error) { + free(error); + } else { + display_one_crtc(reply); + free(reply); + } + } +} + +void display_one_output(xcb_connection_t* connection, xcb_randr_get_output_info_reply_t* output, + xcb_timestamp_t timestamp) { + xcb_randr_get_crtc_info_reply_t* crtc = xcb_randr_get_crtc_info_reply( + connection, xcb_randr_get_crtc_info(connection, output->crtc, timestamp), NULL); + if (!crtc) { + return; + } + display_one_crtc(crtc); + free(crtc); +} + +void display_outputs(xcb_connection_t* connection, + xcb_randr_get_screen_resources_reply_t* resources, xcb_timestamp_t timestamp) { + std::vector cookies; + xcb_generic_error_t* error = nullptr; + + int len = xcb_randr_get_screen_resources_outputs_length(resources); + xcb_randr_output_t* randr_outputs = xcb_randr_get_screen_resources_outputs(resources); + + for (int i = 0; i < len; ++i) { + cookies.push_back(xcb_randr_get_output_info(connection, randr_outputs[i], timestamp)); + } + + for (const auto& cookie : cookies) { + xcb_randr_get_output_info_reply_t* reply = + xcb_randr_get_output_info_reply(connection, cookie, &error); + if (error) { + free(error); + continue; + } else { + display_one_output(connection, reply, timestamp); + free(reply); + } + } +} + +// Lesson of the day: All _reply_t* objects must be freed. +// +// xcb_randr_rotation_t) +// } +// get_rotation(xcb_connection_t* connection, xcb_randr_crtc_t crtc) { +// xcb_generic_error_t* error = nullptr; +// xcb_randr_get_crtc_info_cookie_t cookie = +// xcb_randr_get_crtc_info(connection, crtc, XCB_CURRENT_TIME); +// xcb_randr_get_crtc_info_reply_t* reply = +// xcb_randr_get_crtc_info_reply(connection, cookie, &error); +// xcb_randr_rotation_t rotation = +// (xcb_randr_rotation_t)reply->rotation; free(reply); return rotation; +// } +// + +xcb_randr_query_version_reply_t* getVersion(xcb_connection_t* connection) { + xcb_generic_error_t* error = nullptr; + xcb_randr_query_version_reply_t* version = xcb_randr_query_version_reply( + connection, + xcb_randr_query_version(connection, XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION), + &error); + if (error) { + std::cout << "Error code:" << error->error_code << std::endl; + free(error); + return NULL; + } + + if (!version) { + return NULL; + } + + return version; +} + +void display_screen_info(int screen_id, xcb_screen_t* screen) { + std::cout << "Screen " << screen_id << " (" << screen->width_in_pixels << ", " + << screen->height_in_pixels << ")" << std::endl; +} + +void display_randr_version(xcb_randr_query_version_reply_t* version) { + if (!version) { + std::cout << "Something went wrong retrieving the version number" << std::endl; + exit(0); + } + + std::cout << "Server reports RandR version (" << version->major_version << "." + << version->minor_version << ")" << std::endl; +} + +// This gives us a connection and a screen object. + +int main(int argc, const char* argv[]) { + int default_screen_id; + xcb_connection_t* xConnection = xcb_connect(nullptr, &default_screen_id); + + auto screen = xcb_aux_get_screen(xConnection, default_screen_id); + display_screen_info(default_screen_id, screen); + + xcb_randr_query_version_reply_t* version = getVersion(xConnection); + display_randr_version(version); + + xcb_randr_get_screen_resources_reply_t* screen_resources = + get_screen_resources(xConnection, screen->root); + + xcb_timestamp_t timestamp = screen_resources->config_timestamp; + + display_outputs(xConnection, screen_resources, timestamp); + + free(screen_resources); + xcb_disconnect(xConnection); + return 0; } diff --git a/studies/xcb_good_usage b/studies/xcb_good_usage new file mode 100755 index 0000000..87fa096 Binary files /dev/null and b/studies/xcb_good_usage differ diff --git a/studies/xcb_good_usage.cc b/studies/xcb_good_usage.cc new file mode 100644 index 0000000..d2c3860 --- /dev/null +++ b/studies/xcb_good_usage.cc @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include + +double get_time(void) { + struct timeval timev; + gettimeofday(&timev, NULL); + return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000); +} + +int main() { + int count = 500; + xcb_atom_t* atoms = (xcb_atom_t*)malloc(count * sizeof(atoms)); + xcb_intern_atom_cookie_t* cookies = + (xcb_intern_atom_cookie_t*)malloc(count * sizeof(xcb_intern_atom_cookie_t)); + char** names; + double end, diff_x; + + /* init names */ + names = (char**)malloc(count * sizeof(char*)); + for (int i = 0; i < count; ++i) { + char buf[100]; + sprintf(buf, "NAME%d", i); + names[i] = strdup(buf); + } + + auto start = get_time(); + xcb_connection_t* connection = xcb_connect(NULL, NULL); + for (int i = 0; i < count; ++i) { + cookies[i] = xcb_intern_atom(connection, 0, strlen(names[i]), names[i]); + } + + for (int i = 0; i < count; i++) { + xcb_intern_atom_reply_t* r = xcb_intern_atom_reply(connection, cookies[i], 0); + if (r) { + atoms[i] = r->atom; + free(r); + } + } + xcb_disconnect(connection); + end = get_time(); + + diff_x = end - start; + printf("XCB (good) use time : %f\n", diff_x); + + for (int i = 0; i < count; ++i) + free(names[i]); + free(names); + free(atoms); + free(cookies); + return 0; +} diff --git a/studies/xcb_poor_usage b/studies/xcb_poor_usage new file mode 100755 index 0000000..f237f66 Binary files /dev/null and b/studies/xcb_poor_usage differ diff --git a/studies/xcb_poor_usage.cc b/studies/xcb_poor_usage.cc new file mode 100644 index 0000000..f72d894 --- /dev/null +++ b/studies/xcb_poor_usage.cc @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include + +double get_time(void) { + struct timeval timev; + gettimeofday(&timev, NULL); + return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000); +} + +int main() { + int count = 500; + xcb_atom_t* atoms = (xcb_atom_t*)malloc(count * sizeof(atoms)); + char** names; + double end, diff_x; + + /* init names */ + names = (char**)malloc(count * sizeof(char*)); + for (int i = 0; i < count; ++i) { + char buf[100]; + sprintf(buf, "NAME%d", i); + names[i] = strdup(buf); + } + + auto start = get_time(); + xcb_connection_t* connection = xcb_connect(NULL, NULL); + for (int i = 0; i < count; ++i) { + atoms[i] = xcb_intern_atom_reply( + connection, xcb_intern_atom(connection, 0, strlen(names[i]), names[i]), NULL) + ->atom; + } + xcb_disconnect(connection); + end = get_time(); + + diff_x = end - start; + printf("XCB (poor) use time : %f\n", diff_x); + + for (int i = 0; i < count; ++i) + free(names[i]); + free(names); + return 0; +} diff --git a/studies/xlib_demo b/studies/xlib_demo new file mode 100755 index 0000000..433dbaa Binary files /dev/null and b/studies/xlib_demo differ diff --git a/studies/xlib_demo.cc b/studies/xlib_demo.cc new file mode 100644 index 0000000..dda9217 --- /dev/null +++ b/studies/xlib_demo.cc @@ -0,0 +1,43 @@ +#include +#include +#include +#include +#include + +double get_time(void) { + struct timeval timev; + gettimeofday(&timev, NULL); + return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000); +} + +int main() { + int count = 500; + Atom* atoms_x = (Atom*)malloc(count * sizeof(atoms_x)); + char** names; + double end, diff_x; + + /* init names */ + names = (char**)malloc(count * sizeof(char*)); + for (int i = 0; i < count; ++i) { + char buf[100]; + sprintf(buf, "NAME%d", i); + names[i] = strdup(buf); + } + + auto start = get_time(); + auto disp = XOpenDisplay(getenv("DISPLAY")); + for (int i = 0; i < count; ++i) + atoms_x[i] = XInternAtom(disp, names[i], 0); + end = get_time(); + XCloseDisplay(disp); + + diff_x = end - start; + printf("Xlib use time : %f\n", diff_x); + + free(atoms_x); + + for (int i = 0; i < count; ++i) + free(names[i]); + free(names); + return 0; +}