تغيير إعدادات الشاشة برمجيا

This article is available in English too, check it out here.

الكود: Geming.DisplayMgr.msi

المحتويات

محتويات هذا الموضوع:

  • المحتويات
  • نظرة خاطفة
  • مقدمة
  • معرفة الوضع الحالي
  • معرفة الأوضاع المتاحة
  • تغيير الإعدادات الحالية
  • تدوير الشاشة
  • مثال
  • خاتمة

نظرة خاطفة

درسنا اليوم يتكلم عن كيفية تغيير إعدادات الشاشة برمجيا. سوف نبدأ أولا بإذن الله بشرح المفاهيم المطلوبة لهذا الدرس ثم سوف نتبعها بالأكواد وبالتفصيل اللازم لهذه الأكواد.

سوف نمر أو سوف نتعرض لأربعة أفكار، معرفة الإعدادات الحالية للشاشة، معرفة الأوضاع المتاحة للشاشة، تغيير هذه الأوضاع أو الإعدادات، وأخيرا تدوير (دوران) الشاشة.

أسلوبنا في هذا الدرس أسلوب تسلسلي، فكل فكرة أو كل فقرة من الكود هي مرتبطة بسابقتها.

وأخيرا، يمكنك تحميل برنامج مثال برنامج يقوم بتغيير أوضاع الشاشة.

مقدمة

بداية، ما هي أوضاع الشاشة؟ الوضع أو Mode للشاشة هو عبارة عن أربعة إعدادات أو اختيارات، وهي:

  • الأبعاد Bounds أو Resolution:
    وهي الطول والعرض بالبيكسل Pixel. مثلا 800 x 600 او 1024 x 768.
  • نظام الألوان Color System او Bit Count أو Bits Per Pixel/Pel أو BPP:
    وهو كمية الألوان التي يمكن للشاشة التعامل معها. وهو مثلا 24-bit أو 32-bit ويسميان High Color، أو 16-bit ويسمى True Color. فكلما زاد عدد البتات Bits كلما أصبحت الألوان والصورة أفضل. وأيضا كلما زاد عدد البتات، كلما زادت نسبة الاستهلاك من موارد الجهاز (الذكارة مثلا.)
  • معدل التصحيح Refresh Rate أو كما يسمى Frequency أو Monitor Flicker:
    وهو معدل تصحيح الشاشة أي أن الشاشة تقوم بعمل Refresh لنفسها بنسبة معينة. وهذا المعدل كلما زاد كلما سبب للمستخدم صُداع وإجهاد للعين. في الأفضل يكون هذا المعدل 75-Hertz فأقل.
  • التدوير Rotation أو Orientation:
    وهو كيفية تدوير الشاشة. فربما في بعض الشاشات تجد إمكانية لتدوير الشاشة فتجعلها بزاوية °90 مثلا باتجاه عقارب الساعة وهكذا.

إذا فالأربع إعدادات، الأبعاد Resolution، نظام الألوان Bit Count، معدل التصحيح Refresh Rate، الدوران Orientation، هذه الأربعة تسمى وضع الشاشة Display Mode.

من الأمور المهمة التي يجب معرفتها أنه ليست كل الشاشة مشابهة لبعضها. فإن كارت الفيديو VGA والشاشة نفسها هما الذان يتحكمان في الأوضاع. فعلى سبيل المثال، تختلف الشاشات العادية CRT (شاشات آشعة الكاثود) وشاشات الكريستال LCD تختلفان في الأبعاد، فالأول يكون النسبة بين الطول والعرض تساوي 4:3 بينما الأخرى تختلف فيها هذه النسبة فيأخذ العرض نسبة أكبر منه في شاشات CRT. وأيضا هناك كروت شاشة لا تدعم نظام الألوان 32-bit وتدعم بدلا منه 24-bit.

أيضا يجب عليك معرفة أن لكل شاشة الأوضاع التي تدعمها Display Supported Modes. فهناك شاشات رغم دعمها لنظام الألوان 32-bit مثلا، فهي لا تدعمه مع أبعاد مثل 1024 x 768.

وأيضا فالشاشات لا تدعم اختيارات عشوائية مثل 1000 x 750 حتى ولو كانت بينها تناسب مع نظام الشاشة (4:3).

لهذا فإنه يجب عليك قبل محاولة تغيير الأوضاع برمجيا أن تتأكد من دعم الشاشة لهذا الوضع. فربما الشاشة لا تدعم دوران بدرجة معينة مع أبعاد معينة أو مع نظام ألوان معين، وهكذا.

لمعرفة الأوضاع المتاحة للشاشة، قم بالوصول إلى إعدادات الشاشة Display Settings من لوحة التحكم Control Panel، والتي تكون مشابهة إلى حد كبير لشكل 1.

شكل 1 - Display Settings Dialog

قم بعد ذلك بالضغط على إعدادات متقدمة Advanced Settings لتظهر لك شاشة Advanced Settings كما في الشكل 2.

شكل 2 - Display Advanced Settings Dialog

ومن ثم اضغط على List All Modes كي تظهر أمامك قائمة بالأوضاع المتاحة (للأسف لا يظهر فيها الدوران Orientation.) انظر شكل 2.

شكل 3 - List All Modes Dialog

قم بالرجوع مرة أخرى إلى شاشة Advanced Options وهناك ابحث عن خصائص كارت اللشاشة ومنه ابحث عن الدوران وقم بتجربة تدوير الشاشة 90° باتجاه عقارب الساعة Clockwise.

شكل 5 - Display Settings Dialog Rotated 90degree Clockwise

لاحظ أنه ليست كل الشاشات تدعم الدوران. لاحظ أيضا أن اتجاه عقارب الساعة هذا بالنسبة للشاشة وليس بالنسبة إليك.

للأسف فبيئة الدوت نت لا تدعم الاختيارات الخاصة بالأجزاء الصلبة Hardware من الجهاز، فلهذا سوف نقوم باستخدام دوال الويندوز Windows API كي نقوم بتغيير إعدادات الشاشة. وسوف تكون الدوال التي نحتاجها موجودة بإذن الله في مكتبة user32.dll.

معرفة الوضع الحالي

الدالة الأولى معنا وهي التي سوف نستخدمها لمعرفة الإعدادات أو الوضع الحالي للشاشة والأوضاع الأخرى المتاحة هي دالة EnumDisplaySettings(). وتعريف هذه الدالة كالتالي:

BOOL EnumDisplaySettings(
  LPCTSTR lpszDeviceName,  // display device
  DWORD iModeNum,          // graphics mode
  [In, Out] LPDEVMODE lpDevMode      // graphics mode settings
);

تأخذ هذه الدالة 3 مدخلات:

  • lpszDeviceName:
    تأخذ اسم الشاشة التي تريد أن تعرف معلومات عنها. في الغالب فإن شاشة واحدة تكون متصلة بالجهاز فلهذا يمكنك استخدام القيمة NULL لإحضار معلومات حول الشاشة الرئيسية.
  • iModeNum:
    نوع البيانات التي تريدها. يمكنك استخدام القيمة -1 لتحديد أن البيانات التي نريدها هي البيانات الحالية.
  • lpDevMode:
    عنصر من نوع DEVMODE يحوي جميع البيانات المطلوبة.

كيف تعمل هذه الدالة؟ بداية، فإن المدخل الأول lpszDeviceName سوف يأخذ -في الغالب- قيمة NULL لتحديد الشاشة الرئيسية كمحور للاهتمام.

أما العنصر الثالث وهو عنصر من نوع DEVMODE فعند نجاح هذه الدالة فسوف يكون يحوي البيانات المطلوبة.

أما العنصر الأهم وهو iModeNum فهو يعمل كالتالي: عند تحديد قيمة -1 تقوم باسترجاع الإعدادات الحالية للشاشة وتكون هذه الإعدادات عبارة عن عنصر من نوع DEVMODE.

أما لمعرفة الأوضاع المتاحة للشاشة جميعها فإنك تقوم بوضع الأمر EnumDisplaySettings() في دوارة Loop وتقوم في هذه الدوارة بالنداء على الدالة وفي كل مرة تقوم بزيادة قيمة iModeNum لترجع وضع مختلف. فمثلا، أول دورة تكون القيمة 0 ليرجع الوضع الأول ثم الثانية تكون 1 ليرجع الوضع الثاني، ثم الثالثة تكون 2 ليرجع الوضع الثالث، وهكذا حتى ترجع لك الدالة قيمة FALSE فحينها تكون انتهت جميع الأوضاع المتاحة للشاشة.

إذا، ما هو تركيب العنصر DEVMODE؟ تعريف العنصر DEVMODE هو كالتالي:

لا تقلق حيال هذه التعريفات. هذه التعريفات هي بلغة C وهي خاصة بدوال الويندوز. سوف نقوم بشرح كيفية استخدام هذه الدوال والتعريفات في كود الـ C#.

typedef struct DEVMODE {
  BCHAR  dmDeviceName[CCHDEVICENAME];
  WORD   dmSpecVersion;
  WORD   dmDriverVersion;
  WORD   dmSize;
  WORD   dmDriverExtra;
  DWORD  dmFields;
  union {
    struct {
      short dmOrientation;
      short dmPaperSize;
      short dmPaperLength;
      short dmPaperWidth;
      short dmScale;
      short dmCopies;
      short dmDefaultSource;
      short dmPrintQuality;
    };
    POINTL dmPosition;
    DWORD  dmDisplayOrientation;
    DWORD  dmDisplayFixedOutput;
  };

  short  dmColor;
  short  dmDuplex;
  short  dmYResolution;
  short  dmTTOption;
  short  dmCollate;
  BYTE  dmFormName[CCHFORMNAME];
  WORD  dmLogPixels;
  DWORD  dmBitsPerPel;
  DWORD  dmPelsWidth;
  DWORD  dmPelsHeight;
  union {
    DWORD  dmDisplayFlags;
    DWORD  dmNup;
  }
  DWORD  dmDisplayFrequency;
#if(WINVER >= 0x0400)
  DWORD  dmICMMethod;
  DWORD  dmICMIntent;
  DWORD  dmMediaType;
  DWORD  dmDitherType;
  DWORD  dmReserved1;
  DWORD  dmReserved2;
#if (WINVER >= 0x0500) || (_WIN32_WINNT >= 0x0400)
  DWORD  dmPanningWidth;
  DWORD  dmPanningHeight;
#endif
#endif /* WINVER >= 0x0400 */
}

هذا العنصر عبارة عن Structure يمهنا منه فقط 5 أعضاء/متغيرات Members:

  • dmPelsWidth و dmPelsHeight:
    الأبعاد (الطول والعرض) للشاشة.
  • dmBitsPerPels:
    نظام الألوان (عدد البتات Bits.)
  • dmDisplayOrientation:
    تدوير الشاشة. يمكنها أن تكون قيمة من 4 قيم:
    • DMDO_DEFAULT = 0
      ليس هناك أي تدوير.
    • DMDO_90 = 1
      تدوير 90° باتجاه عقارب الساعة.
    • DMDO_180 = 2
      تدوير 180° باتجاه عقارب الساعة.
    • DMDO_270 = 3
      تدوير 270° باتجاه عقارب الساعة.
  • dmDisplayFrequency:
    معدل التصحيح Frequency بالهرتز Hertz.

الآن يأتي دور الدوت نت أو C# بالأخص.كي يمكنك استخدام دوال أو عناصر ويندوز Windows API في برنامجك يجب أن تقوم بتعريفها.

فالتالي هو تعريف الدالة EnumDisplaySettings() في C#:

لا تنسى إضافة سطر using للـ Namespace المسماة System.Runtime.Interoperability وهي لازمة لجميع الأكواد في هذا الدرس.

    [DllImport("User32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern Boolean EnumDisplaySettings(
        [param: MarshalAs(UnmanagedType.LPTStr)]
        string lpszDeviceName,
        [param: MarshalAs(UnmanagedType.U4)]
        int iModeNum,
        [In, Out]
        ref DEVMODE lpDevMode);

وهذا هو تعريف العناصر Structures التي تستخدمها الدالة:

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct DEVMODE
    {
        // You can define the following constant
        // but OUTSIDE the structure because you know
        // that size and layout of the structure is very important
        // CCHDEVICENAME = 32 = 0x50
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string dmDeviceName;
        // In addition you can define the last character array
        // as following:
        //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
        //public Char[] dmDeviceName;

        // After the 32-bytes array
        [MarshalAs(UnmanagedType.U2)]
        public UInt16 dmSpecVersion;

        [MarshalAs(UnmanagedType.U2)]
        public UInt16 dmDriverVersion;

        [MarshalAs(UnmanagedType.U2)]
        public UInt16 dmSize;

        [MarshalAs(UnmanagedType.U2)]
        public UInt16 dmDriverExtra;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmFields;

        public POINTL dmPosition;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmDisplayOrientation;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmDisplayFixedOutput;

        [MarshalAs(UnmanagedType.I2)]
        public Int16 dmColor;

        [MarshalAs(UnmanagedType.I2)]
        public Int16 dmDuplex;

        [MarshalAs(UnmanagedType.I2)]
        public Int16 dmYResolution;

        [MarshalAs(UnmanagedType.I2)]
        public Int16 dmTTOption;

        [MarshalAs(UnmanagedType.I2)]
        public Int16 dmCollate;

        // CCHDEVICENAME = 32 = 0x50
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string dmFormName;
        // Also can be defined as
        //[MarshalAs(UnmanagedType.ByValArray,
        //    SizeConst = 32, ArraySubType = UnmanagedType.U1)]
        //public Byte[] dmFormName;

        [MarshalAs(UnmanagedType.U2)]
        public UInt16 dmLogPixels;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmBitsPerPel;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmPelsWidth;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmPelsHeight;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmDisplayFlags;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmDisplayFrequency;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmICMMethod;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmICMIntent;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmMediaType;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmDitherType;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmReserved1;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmReserved2;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmPanningWidth;

        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmPanningHeight;
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct POINTL
    {
        [MarshalAs(UnmanagedType.I4)]
        public int x;
        [MarshalAs(UnmanagedType.I4)]
        public int y;
    }

العنصر DEVMODE هو عنصر معقد جدا إلى أبعد درجة ويختلف باختلاف نظام التشغيل، ولهذا فإننا افترضنا أن المستخدم يعمل على Windows XP او إصدار أعلى وأن نظام تشغيله هو 32-bit.

هنا يظهر سؤال: من أين لنا بالعنصر POINTL؟ إذا رجعنا إلى التعريف الأصلي للعنصر DEVMODE فسوف نجد أنه يحوي على أعضاء Members من نوع POINTL فلذلك يجب تعريف هذا العنصر أيضا.

والآن إلى النقطة الأكثر إثارة. الكود التالي يقوم باسترجاع إعدادات الشاشة الحالية ويقوم بطباعتها على الشاشة.

public static void GetCurrentSettings()
{
    DEVMODE mode = new DEVMODE();
    mode.dmSize = (ushort)Marshal.SizeOf(mode);

    if (EnumDisplaySettings(null, -1, ref mode) == true) // Succeeded
    {
        Console.WriteLine("Current Mode:nt" +
            "{0} by {1}, " +
            "{2} bit, " +
            "{3} degrees, " +
            "{4} hertz",
            mode.dmPelsWidth,
            mode.dmPelsHeight,
            mode.dmBitsPerPel,
            mode.dmDisplayOrientation * 90,
            mode.dmDisplayFrequency);
    }
}

معرفة الأوضاع المتاحة

ماذا عن الأوضاع المتاحة للشاشة؟ بنفس الطريقة سوف نقوم بالحصول عليها ولكن سوف نستخدم رقم الوضع بدلا من الرقم -1 الخاص بالوضع الحالي.

public static void EnumerateSupportedModes()
{
    DEVMODE mode = new DEVMODE();
    mode.dmSize = (ushort)Marshal.SizeOf(mode);

    int modeIndex = 0; // 0 = The first mode

    Console.WriteLine("Supported Modes:");

    while (EnumDisplaySettings(null,
        modeIndex,
        ref mode) == true) // Mode found
    {
        Console.WriteLine("t" +
            "{0} by {1}, " +
            "{2} bit, " +
            "{3} degrees, " +
            "{4} hertz",
            mode.dmPelsWidth,
            mode.dmPelsHeight,
            mode.dmBitsPerPel,
            mode.dmDisplayOrientation * 90,
            mode.dmDisplayFrequency);

        modeIndex++; // The next mode
    }
}

لاحظ استخدامنا لعداد في كل دورة، ولاحظ أيضا اعتمادنا على القيمة التي ترجعها الدالة.

تغيير الإعدادات الحالية

فهمت الفكرة؟ الآن ننتقل إلى جزء آخر من الموضوع، وهو كيفية تغيير إعدادات الشاشة. ويتم هذا عن طريق دالة أخرى وهي ChangeDisplaySettings() وهذه الدالة تعريفها كالتالي:

LONG ChangeDisplaySettings(
  LPDEVMODE lpDevMode,  // graphics mode
  DWORD dwflags         // graphics mode options
);

تأخذ هذه الدالة مدخلين:

  • lpDevMode:
    عنصر من نوع DEVMODE يحتوي على الإعدادات الجديدة.
  • dwflags:
    اختيارات متقدمة ليس لها حاجة في موضوعنا اليوم، لهذا سوف تأخذ القيمة صفر 0.

ولكن كيف نملأ العنصر lpDevMode حيث أنه يحتوي على عدد ضخم من المتغيرات؟ كما ذكرنا سابقا، أنت لا تحتاج إلى التعامل مع جميع هذه العناصر، فقط الـ 5 عناصر الأهم بالنسبة إلينا. فسياسة هذه الدالة ChangeDisplaySettings() كالتالي: تقوم باسترجاع الإعدادات الحالية للشاشة عن طريق الدالة EnumDisplaySettings() كي تملأ المتغيرات الخاصة بـ lpDevMode، ثم تقوم بعد ذلك بتغيير المتغيرات خاصتك التي تحتاجها مثل dmPelsHeight و dmPelsWidth لتغيير الأبعاد. وأخيرا تقوم بالنداء على هذه الدالة ChangeDisplaySettings() ليتم تطبيق إعداداتك.

يجب الملاحظة أن هذه الدالة تقوم بإرجاع قيمة رقمية تمثل حالة العملية التي تم تطبيقها وهي تكون واحدة من أربعة:

  • DISP_CHANGE_SUCCESSFUL = 0
    معناها أن العملية نجحت.
  • DISP_CHANGE_BADMODE = -2
    معناها أن الوضع Mode المختار لا تدعمه الشاشة، مثلا دوران مع أبعاد معينة، أو أبعاد غير موجودة مثل 1000 x 750 مثلا.
  • DISP_CHANGE_FAILED = -1
    العملية فشلت لسبب غير معروف.
  • DISP_CHANGE_RESTART = 1
    يجب إعادة تشغيل الجهاز حتى يتم تطبيق الاختيارات. بعد انتهاء عصر Windows 9x فإن هذه الدالة لا تقوم بإرجاع هذه القيمة.

والآن إلى الكود. الكود التالي هو تعريف هذه الدالة في كي نستطيع استخدامها في الـ C#:

    [DllImport("User32.dll")]
    [return: MarshalAs(UnmanagedType.I4)]
    public static extern int ChangeDisplaySettings(
        [In, Out]
        ref DEVMODE lpDevMode,
        [param: MarshalAs(UnmanagedType.U4)]
        uint dwflags);

والآن إلى إثارة أكثر، الكود التالي يقوم بتغيير إعدادات الشاشة إلى 800 x 600 بنظام ألوان 16-bit:

const int DISP_CHANGE_SUCCESSFUL = 0;
const int DISP_CHANGE_RESTART = 1;
const int DISP_CHANGE_FAILED = -1;
const int DISP_CHANGE_BADMODE = -2;

static void Main()
{
    // Changing the display resolution
    // to 800 by 600
    // and the color system (bit count)
    // to 16-bit
    ChangeDisplaySettings(800, 600, 16);
}

public static void ChangeDisplaySettings(int width, int height, int bitCount)
{
    DEVMODE originalMode = new DEVMODE();
    originalMode.dmSize = (ushort)Marshal.SizeOf(originalMode);

    // Retrieving current settings
    // to edit them
    EnumDisplaySettings(null, ENUM_CURRENT_SETTINGS, ref originalMode);

    // Making a copy of the current settings
    // to allow reseting to the original mode
    DEVMODE newMode = originalMode;

    // Changing the settings
    newMode.dmPelsWidth = (uint)width;
    newMode.dmPelsHeight = (uint)height;
    newMode.dmBitsPerPel = (uint)bitCount;

    // Capturing the operation result
    int result = ChangeDisplaySettings(ref newMode, 0);

    if (result == DISP_CHANGE_SUCCESSFUL)
    {
        Console.WriteLine("Succeeded.n");

        // Inspecting the new mode
        GetCurrentSettings();

        Console.WriteLine();

        // Waiting for seeing the results
        Console.ReadKey(true);

        ChangeDisplaySettings(ref originalMode, 0);
    }
    else if (result == DISP_CHANGE_BADMODE)
        Console.WriteLine("Mode not supported.");
    else if (result == DISP_CHANGE_RESTART)
        Console.WriteLine("Restart required.");
    else
        Console.WriteLine("Failed. Error code = {0}", result);
}

تدوير الشاشة

وإلى النقطة الأكثر إثارة ومتعة في موضوعنا، وهي تدوير الشاشة. الكود الذي استخدمناه سابقا قام بتغيير الإعدادات مثل الأبعاد ونظام الألوان. لماذا لا نقوم الآن بتغيير التدوير عن طريق المتغير dmDisplayOrientation مثلما فعلنا في تغيير باقي الإعدادات؟ هذا ما يقوم به الكود التالي:

الكود مختصر للإفادة.

    static void Main()
    {
        // 0 degrees ( DMDO_DEFAULT = 0 )

        Console.WriteLine("Press any key to rotate the screen . . .");
        Console.ReadKey(true);
        Console.WriteLine();

        RotateScreen(true); // 90 degrees (	DMDO_90 = 1 )
        Console.WriteLine ("Press any key to rotate the screen . . .");
        Console.ReadKey(true);
        Console.WriteLine();

        RotateScreen(true); // 180 degrees ( DMDO_180 = 2 )
        Console.WriteLine("Press any key to rotate the screen . . .");
        Console.ReadKey(true);
        Console.WriteLine();

        RotateScreen(true); // 270 degrees ( DMDO_270 = 3 )
        Console.WriteLine("Press any key to rotate the screen . . .");
        Console.ReadKey(true);
        Console.WriteLine();

        RotateScreen(true); // 0 degrees ( DMDO_DEFAULT = 0 )
    }

    public static void RotateScreen(bool clockwise)
    {
        // Retrieving current settings
        // ...

        // Rotating the screen
        if (clockwise)
            if (newMode.dmDisplayOrientation  DMDO_DEFAULT)
                newMode.dmDisplayOrientation--;
            else
                newMode.dmDisplayOrientation = DMDO_270;

        // Swapping width and height;
        uint temp = newMode.dmPelsWidth;
        newMode.dmPelsWidth = newMode.dmPelsHeight;
        newMode.dmPelsHeight = temp;

        // Capturing the operation result
        // ...
    }

مثال

يمكنك تحميل برنامجنا Display Settings Sample والذي يقوم بتغيير إعدادات الشاشة وتدويرها وما إلى غير ذلك.

شكل 5 - Display Settings Sample

التحميل من هنا

خاتمة

كان هذا درسنا اليوم، طويلا ومعقدا قليلا (أو كثيرا!) ولكن استفدنا الكثير.

أطيب الأمنيات.

اترك تعليقا

إملأ الحقول أدناه بالمعلومات المناسبة أو إضغط على إحدى الأيقونات لتسجيل الدخول:

شعار ووردبريس.كوم

أنت تعلق بإستخدام حساب WordPress.com. تسجيل خروج   /  تغيير )

صورة تويتر

أنت تعلق بإستخدام حساب Twitter. تسجيل خروج   /  تغيير )

Facebook photo

أنت تعلق بإستخدام حساب Facebook. تسجيل خروج   /  تغيير )

Connecting to %s