예제 #1
1
  @Override
  protected void onDestroy() {
    Log.v("SDL", "onDestroy()");

    if (SDLActivity.mBrokenLibraries) {
      super.onDestroy();
      // Reset everything in case the user re opens the app
      SDLActivity.initialize();
      return;
    }

    // Send a quit message to the application
    SDLActivity.mExitCalledFromJava = true;
    SDLActivity.nativeQuit();

    // Now wait for the SDL thread to quit
    if (SDLActivity.mSDLThread != null) {
      try {
        SDLActivity.mSDLThread.join();
      } catch (Exception e) {
        Log.v("SDL", "Problem stopping thread: " + e);
      }
      SDLActivity.mSDLThread = null;

      // Log.v("SDL", "Finished waiting for SDL thread");
    }

    super.onDestroy();
    // Reset everything in case the user re opens the app
    SDLActivity.initialize();
  }
예제 #2
0
  // Touch events
  @Override
  public boolean onTouch(View v, MotionEvent event) {
    final int touchDevId = event.getDeviceId();
    final int pointerCount = event.getPointerCount();
    // touchId, pointerId, action, x, y, pressure
    int actionPointerIndex =
        (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK)
            >> MotionEvent.ACTION_POINTER_ID_SHIFT; /* API 8: event.getActionIndex(); */
    int pointerFingerId = event.getPointerId(actionPointerIndex);
    int action =
        (event.getAction() & MotionEvent.ACTION_MASK); /* API 8: event.getActionMasked(); */

    float x = event.getX(actionPointerIndex) / mWidth;
    float y = event.getY(actionPointerIndex) / mHeight;
    float p = event.getPressure(actionPointerIndex);

    if (action == MotionEvent.ACTION_MOVE && pointerCount > 1) {
      // TODO send motion to every pointer if its position has
      // changed since prev event.
      for (int i = 0; i < pointerCount; i++) {
        pointerFingerId = event.getPointerId(i);
        x = event.getX(i) / mWidth;
        y = event.getY(i) / mHeight;
        p = event.getPressure(i);
        SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
      }
    } else {
      SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
    }
    return true;
  }
예제 #3
0
  // Key events
  @Override
  public boolean onKey(View v, int keyCode, KeyEvent event) {
    // Dispatch the different events depending on where they come from
    // Some SOURCE_DPAD or SOURCE_GAMEPAD are also SOURCE_KEYBOARD
    // So, we try to process them as DPAD or GAMEPAD events first, if that fails we try them as
    // KEYBOARD

    if ((event.getSource() & InputDevice.SOURCE_GAMEPAD) != 0
        || (event.getSource() & InputDevice.SOURCE_DPAD) != 0) {
      if (event.getAction() == KeyEvent.ACTION_DOWN) {
        if (SDLActivity.onNativePadDown(event.getDeviceId(), keyCode) == 0) {
          return true;
        }
      } else if (event.getAction() == KeyEvent.ACTION_UP) {
        if (SDLActivity.onNativePadUp(event.getDeviceId(), keyCode) == 0) {
          return true;
        }
      }
    }

    if ((event.getSource() & InputDevice.SOURCE_KEYBOARD) != 0) {
      if (event.getAction() == KeyEvent.ACTION_DOWN) {
        // Log.v("SDL", "key down: " + keyCode);
        SDLActivity.onNativeKeyDown(keyCode);
        return true;
      } else if (event.getAction() == KeyEvent.ACTION_UP) {
        // Log.v("SDL", "key up: " + keyCode);
        SDLActivity.onNativeKeyUp(keyCode);
        return true;
      }
    }

    return false;
  }
예제 #4
0
 // Called when we lose the surface
 @Override
 public void surfaceDestroyed(SurfaceHolder holder) {
   Log.v("SDL", "surfaceDestroyed()");
   // Call this *before* setting mIsSurfaceReady to 'false'
   SDLActivity.handlePause();
   SDLActivity.mIsSurfaceReady = false;
   SDLActivity.onNativeSurfaceDestroyed();
 }
예제 #5
0
  @Override
  public void run() {
    // Runs SDL_main()
    SDLActivity.nativeInit(((Activity) SDLActivity.getContext()).getFilesDir().getAbsolutePath());

    // Log.v("SDL", "SDL thread terminated");
    // Urho3D: finish activity when SDL_main returns
    SDLActivity.finishActivity();
  }
예제 #6
0
  // Called when the surface is resized
  @Override
  public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    Log.v("SDL", "surfaceChanged()");

    int sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565 by default
    switch (format) {
      case PixelFormat.A_8:
        Log.v("SDL", "pixel format A_8");
        break;
      case PixelFormat.LA_88:
        Log.v("SDL", "pixel format LA_88");
        break;
      case PixelFormat.L_8:
        Log.v("SDL", "pixel format L_8");
        break;
      case PixelFormat.RGBA_4444:
        Log.v("SDL", "pixel format RGBA_4444");
        sdlFormat = 0x15421002; // SDL_PIXELFORMAT_RGBA4444
        break;
      case PixelFormat.RGBA_5551:
        Log.v("SDL", "pixel format RGBA_5551");
        sdlFormat = 0x15441002; // SDL_PIXELFORMAT_RGBA5551
        break;
      case PixelFormat.RGBA_8888:
        Log.v("SDL", "pixel format RGBA_8888");
        sdlFormat = 0x16462004; // SDL_PIXELFORMAT_RGBA8888
        break;
      case PixelFormat.RGBX_8888:
        Log.v("SDL", "pixel format RGBX_8888");
        sdlFormat = 0x16261804; // SDL_PIXELFORMAT_RGBX8888
        break;
      case PixelFormat.RGB_332:
        Log.v("SDL", "pixel format RGB_332");
        sdlFormat = 0x14110801; // SDL_PIXELFORMAT_RGB332
        break;
      case PixelFormat.RGB_565:
        Log.v("SDL", "pixel format RGB_565");
        sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565
        break;
      case PixelFormat.RGB_888:
        Log.v("SDL", "pixel format RGB_888");
        // Not sure this is right, maybe SDL_PIXELFORMAT_RGB24 instead?
        sdlFormat = 0x16161804; // SDL_PIXELFORMAT_RGB888
        break;
      default:
        Log.v("SDL", "pixel format unknown " + format);
        break;
    }

    mWidth = width;
    mHeight = height;
    SDLActivity.onNativeResize(width, height, sdlFormat);
    Log.v("SDL", "Window size:" + width + "x" + height);

    SDLActivity.startApp();
  }
예제 #7
0
  @Override
  public void pollInputDevices() {
    int[] deviceIds = InputDevice.getDeviceIds();
    // It helps processing the device ids in reverse order
    // For example, in the case of the XBox 360 wireless dongle,
    // so the first controller seen by SDL matches what the receiver
    // considers to be the first controller

    for (int i = deviceIds.length - 1; i > -1; i--) {
      SDLJoystick joystick = getJoystick(deviceIds[i]);
      if (joystick == null) {
        joystick = new SDLJoystick();
        InputDevice joystickDevice = InputDevice.getDevice(deviceIds[i]);
        if ((joystickDevice.getSources() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
          joystick.device_id = deviceIds[i];
          joystick.name = joystickDevice.getName();
          joystick.axes = new ArrayList<InputDevice.MotionRange>();

          for (InputDevice.MotionRange range : joystickDevice.getMotionRanges()) {
            if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
              joystick.axes.add(range);
            }
          }

          mJoysticks.add(joystick);
          SDLActivity.nativeAddJoystick(
              joystick.device_id, joystick.name, 0, -1, joystick.axes.size(), 0, 0);
        }
      }
    }

    /* Check removed devices */
    ArrayList<Integer> removedDevices = new ArrayList<Integer>();
    for (int i = 0; i < mJoysticks.size(); i++) {
      int device_id = mJoysticks.get(i).device_id;
      int j;
      for (j = 0; j < deviceIds.length; j++) {
        if (device_id == deviceIds[j]) break;
      }
      if (j == deviceIds.length) {
        removedDevices.add(device_id);
      }
    }

    for (int i = 0; i < removedDevices.size(); i++) {
      int device_id = removedDevices.get(i);
      SDLActivity.nativeRemoveJoystick(device_id);
      for (int j = 0; j < mJoysticks.size(); j++) {
        if (mJoysticks.get(j).device_id == device_id) {
          mJoysticks.remove(j);
          break;
        }
      }
    }
  }
예제 #8
0
  // Key events
  public boolean onKey(View v, int keyCode, KeyEvent event) {

    if (event.getAction() == KeyEvent.ACTION_DOWN) {
      // Log.v("SDL", "key down: " + keyCode);
      SDLActivity.onNativeKeyDown(keyCode);
      return true;
    } else if (event.getAction() == KeyEvent.ACTION_UP) {
      // Log.v("SDL", "key up: " + keyCode);
      SDLActivity.onNativeKeyUp(keyCode);
      return true;
    }

    return false;
  }
예제 #9
0
  @Override
  public void run() {
    // Runs SDL_main()
    SDLActivity.nativeInit(SDLActivity.mSingleton.getArguments());

    // Log.v("SDL", "SDL thread terminated");
  }
예제 #10
0
 @Override
 public void onSensorChanged(SensorEvent event) {
   if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
     float x, y;
     switch (mDisplay.getRotation()) {
       case Surface.ROTATION_90:
         x = -event.values[1];
         y = event.values[0];
         break;
       case Surface.ROTATION_270:
         x = event.values[1];
         y = -event.values[0];
         break;
       case Surface.ROTATION_180:
         x = -event.values[1];
         y = -event.values[0];
         break;
       default:
         x = event.values[0];
         y = event.values[1];
         break;
     }
     SDLActivity.onNativeAccel(
         -x / SensorManager.GRAVITY_EARTH,
         y / SensorManager.GRAVITY_EARTH,
         event.values[2] / SensorManager.GRAVITY_EARTH - 1);
   }
 }
예제 #11
0
  // EGL functions
  public static boolean initEGL(int majorVersion, int minorVersion, int[] attribs) {
    try {
      if (SDLActivity.mEGLDisplay == null) {
        Log.v("SDL", "Starting up OpenGL ES " + majorVersion + "." + minorVersion);

        EGL10 egl = (EGL10) EGLContext.getEGL();

        EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);

        int[] version = new int[2];
        egl.eglInitialize(dpy, version);

        EGLConfig[] configs = new EGLConfig[1];
        int[] num_config = new int[1];
        if (!egl.eglChooseConfig(dpy, attribs, configs, 1, num_config) || num_config[0] == 0) {
          Log.e("SDL", "No EGL config available");
          return false;
        }
        EGLConfig config = configs[0];

        SDLActivity.mEGLDisplay = dpy;
        SDLActivity.mEGLConfig = config;
        SDLActivity.mGLMajor = majorVersion;
        SDLActivity.mGLMinor = minorVersion;
      }
      return SDLActivity.createEGLSurface();

    } catch (Exception e) {
      Log.v("SDL", e + "");
      for (StackTraceElement s : e.getStackTrace()) {
        Log.v("SDL", s.toString());
      }
      return false;
    }
  }
예제 #12
0
 /**
  * Called by onResume or surfaceCreated. An actual resume should be done only when the surface is
  * ready. Note: Some Android variants may send multiple surfaceChanged events, so we don't need to
  * resume every time we get one of those events, only if it comes after surfaceDestroyed
  */
 public static void handleResume() {
   if (SDLActivity.mIsPaused && SDLActivity.mIsSurfaceReady && SDLActivity.mHasFocus) {
     SDLActivity.mIsPaused = false;
     SDLActivity.nativeResume();
     mSurface.handleResume();
   }
 }
예제 #13
0
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
    vibratorActive = v.hasVibrator();

    // Setting up File Selection handlers and callbacks
    final PlutoboyActivity pba = this;
    final FileSelectedListener fl =
        new FileSelectedListener() {
          public void onFileSelected(File file) {
            lock.lock();
            try {
              fileSelectedPath = file.getAbsolutePath();
              fileSelectedB = true;
              fileSelected.signal();
            } finally {
              lock.unlock();
            }
          }
        };

    fileDialogHandler =
        new Handler() {
          public void handleMessage(Message msg) {
            SimpleFileChooser fc =
                new SimpleFileChooser(pba, Environment.getExternalStorageDirectory(), fl);
            fc.showDialog();
          }
        };
  }
예제 #14
0
 /**
  * Called by onPause or surfaceDestroyed. Even if surfaceDestroyed is the first to be called,
  * mIsSurfaceReady should still be set to 'true' during the call to onPause (in a usual scenario).
  */
 public static void handlePause() {
   if (!SDLActivity.mIsPaused && SDLActivity.mIsSurfaceReady) {
     SDLActivity.mIsPaused = true;
     SDLActivity.nativePause();
     mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, false);
   }
 }
예제 #15
0
 /**
  * Called by onResume or surfaceCreated. An actual resume should be done only when the surface is
  * ready. Note: Some Android variants may send multiple surfaceChanged events, so we don't need to
  * resume every time we get one of those events, only if it comes after surfaceDestroyed
  */
 public static void handleResume() {
   if (SDLActivity.mIsPaused && SDLActivity.mIsSurfaceReady && SDLActivity.mHasFocus) {
     SDLActivity.mIsPaused = false;
     SDLActivity.nativeResume();
     mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, true);
   }
 }
예제 #16
0
 @Override
 public boolean handleMotionEvent(MotionEvent event) {
   if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
     int actionPointerIndex = event.getActionIndex();
     int action = event.getActionMasked();
     switch (action) {
       case MotionEvent.ACTION_MOVE:
         SDLJoystick joystick = getJoystick(event.getDeviceId());
         if (joystick != null) {
           for (int i = 0; i < joystick.axes.size(); i++) {
             InputDevice.MotionRange range = joystick.axes.get(i);
             /* Normalize the value to -1...1 */
             float value =
                 (event.getAxisValue(range.getAxis(), actionPointerIndex) - range.getMin())
                         / range.getRange()
                         * 2.0f
                     - 1.0f;
             SDLActivity.onNativeJoy(joystick.device_id, i, value);
           }
         }
         break;
       default:
         break;
     }
   }
   return true;
 }
예제 #17
0
  @Override
  public void run() {
    // Runs SDL_main()
    SDLActivity.nativeInit();

    // Log.v("SDL", "SDL thread terminated");
  }
예제 #18
0
  // Touch events
  @Override
  public boolean onTouch(View v, MotionEvent event) {
    /* Ref: http://developer.android.com/training/gestures/multi.html */
    final int touchDevId = event.getDeviceId();
    final int pointerCount = event.getPointerCount();
    int action = event.getActionMasked();
    int pointerFingerId;
    int i = -1;
    float x, y, p;

    switch (action) {
      case MotionEvent.ACTION_MOVE:
        for (i = 0; i < pointerCount; i++) {
          pointerFingerId = event.getPointerId(i);
          x = event.getX(i) / mWidth;
          y = event.getY(i) / mHeight;
          p = event.getPressure(i);
          SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
        }
        break;

      case MotionEvent.ACTION_UP:
      case MotionEvent.ACTION_DOWN:
        // Primary pointer up/down, the index is always zero
        i = 0;
      case MotionEvent.ACTION_POINTER_UP:
      case MotionEvent.ACTION_POINTER_DOWN:
        // Non primary pointer up/down
        if (i == -1) {
          i = event.getActionIndex();
        }

        pointerFingerId = event.getPointerId(i);
        x = event.getX(i) / mWidth;
        y = event.getY(i) / mHeight;
        p = event.getPressure(i);
        SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
        break;

      default:
        break;
    }

    return true;
  }
예제 #19
0
 // Called when we lose the surface
 @Override
 public void surfaceDestroyed(SurfaceHolder holder) {
   Log.v("SDL", "surfaceDestroyed()");
   if (!SDLActivity.mIsPaused) {
     SDLActivity.mIsPaused = true;
     SDLActivity.nativePause();
   }
   enableSensor(Sensor.TYPE_ACCELEROMETER, false);
 }
예제 #20
0
 @Override
 public void onSensorChanged(SensorEvent event) {
   if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
     SDLActivity.onNativeAccel(
         event.values[0] / SensorManager.GRAVITY_EARTH,
         event.values[1] / SensorManager.GRAVITY_EARTH,
         event.values[2] / SensorManager.GRAVITY_EARTH);
   }
 }
예제 #21
0
  @Override
  public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    Log.v("SDL", "onWindowFocusChanged(): " + hasFocus);

    SDLActivity.mHasFocus = hasFocus;
    if (hasFocus) {
      SDLActivity.handleResume();
    }
  }
예제 #22
0
  // Called when the surface is resized
  public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    // Log.v("SDL", "surfaceChanged()");

    int sdlFormat = 0x85151002; // SDL_PIXELFORMAT_RGB565 by default
    switch (format) {
      case PixelFormat.A_8:
        Log.v("SDL", "pixel format A_8");
        break;
      case PixelFormat.LA_88:
        Log.v("SDL", "pixel format LA_88");
        break;
      case PixelFormat.L_8:
        Log.v("SDL", "pixel format L_8");
        break;
      case PixelFormat.RGBA_4444:
        Log.v("SDL", "pixel format RGBA_4444");
        sdlFormat = 0x85421002; // SDL_PIXELFORMAT_RGBA4444
        break;
      case PixelFormat.RGBA_5551:
        Log.v("SDL", "pixel format RGBA_5551");
        sdlFormat = 0x85441002; // SDL_PIXELFORMAT_RGBA5551
        break;
      case PixelFormat.RGBA_8888:
        Log.v("SDL", "pixel format RGBA_8888");
        sdlFormat = 0x86462004; // SDL_PIXELFORMAT_RGBA8888
        break;
      case PixelFormat.RGBX_8888:
        Log.v("SDL", "pixel format RGBX_8888");
        sdlFormat = 0x86262004; // SDL_PIXELFORMAT_RGBX8888
        break;
      case PixelFormat.RGB_332:
        Log.v("SDL", "pixel format RGB_332");
        sdlFormat = 0x84110801; // SDL_PIXELFORMAT_RGB332
        break;
      case PixelFormat.RGB_565:
        Log.v("SDL", "pixel format RGB_565");
        sdlFormat = 0x85151002; // SDL_PIXELFORMAT_RGB565
        break;
      case PixelFormat.RGB_888:
        Log.v("SDL", "pixel format RGB_888");
        // Not sure this is right, maybe SDL_PIXELFORMAT_RGB24 instead?
        sdlFormat = 0x86161804; // SDL_PIXELFORMAT_RGB888
        break;
      default:
        Log.v("SDL", "pixel format unknown " + format);
        break;
    }
    SDLActivity.onNativeResize(width, height, sdlFormat);

    // Now start up the C app thread
    if (mSDLThread == null) {
      mSDLThread = new Thread(new SDLMain(), "SDLThread");
      mSDLThread.start();
    }
  }
예제 #23
0
  @Override
  protected void onResume() {
    Log.v("SDL", "onResume()");
    super.onResume();

    if (SDLActivity.mBrokenLibraries) {
      return;
    }

    SDLActivity.handleResume();
  }
예제 #24
0
  // Key events
  @Override
  public boolean onKey(View v, int keyCode, KeyEvent event) {

    // Urho3D: let the home & volume keys be handled by the system
    if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
        || keyCode == KeyEvent.KEYCODE_VOLUME_UP
        || keyCode == KeyEvent.KEYCODE_HOME) return false;

    if (event.getAction() == KeyEvent.ACTION_DOWN) {
      // Log.v("SDL", "key down: " + keyCode);
      SDLActivity.onNativeKeyDown(keyCode);
      return true;
    } else if (event.getAction() == KeyEvent.ACTION_UP) {
      // Log.v("SDL", "key up: " + keyCode);
      SDLActivity.onNativeKeyUp(keyCode);
      return true;
    }

    return false;
  }
예제 #25
0
  // Touch events
  public boolean onTouch(View v, MotionEvent event) {

    int action = event.getAction();
    float x = event.getX();
    float y = event.getY();
    float p = event.getPressure();

    // TODO: Anything else we need to pass?
    SDLActivity.onNativeTouch(action, x, y, p);
    return true;
  }
예제 #26
0
  @Override
  public void onLowMemory() {
    Log.v("SDL", "onLowMemory()");
    super.onLowMemory();

    if (SDLActivity.mBrokenLibraries) {
      return;
    }

    SDLActivity.nativeLowMemory();
  }
예제 #27
0
  @Override
  public boolean sendKeyEvent(KeyEvent event) {

    /*
     * This handles the keycodes from soft keyboard (and IME-translated
     * input from hardkeyboard)
     */
    int keyCode = event.getKeyCode();
    if (event.getAction() == KeyEvent.ACTION_DOWN) {
      if (event.isPrintingKey()) {
        commitText(String.valueOf((char) event.getUnicodeChar()), 1);
      }
      SDLActivity.onNativeKeyDown(keyCode);
      return true;
    } else if (event.getAction() == KeyEvent.ACTION_UP) {

      SDLActivity.onNativeKeyUp(keyCode);
      return true;
    }
    return super.sendKeyEvent(event);
  }
예제 #28
0
  @Override
  public boolean onKey(View v, int keyCode, KeyEvent event) {

    // This handles the hardware keyboard input
    if (event.isPrintingKey()) {
      if (event.getAction() == KeyEvent.ACTION_DOWN) {
        ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1);
      }
      return true;
    }

    if (event.getAction() == KeyEvent.ACTION_DOWN) {
      SDLActivity.onNativeKeyDown(keyCode);
      return true;
    } else if (event.getAction() == KeyEvent.ACTION_UP) {
      SDLActivity.onNativeKeyUp(keyCode);
      return true;
    }

    return false;
  }
예제 #29
0
  // Generic Motion (mouse hover, joystick...) events go here
  @Override
  public boolean onGenericMotion(View v, MotionEvent event) {
    float x, y;
    int mouseButton;
    int action;

    switch (event.getSource()) {
      case InputDevice.SOURCE_JOYSTICK:
      case InputDevice.SOURCE_GAMEPAD:
      case InputDevice.SOURCE_DPAD:
        SDLActivity.handleJoystickMotionEvent(event);
        return true;

      case InputDevice.SOURCE_MOUSE:
        action = event.getActionMasked();
        switch (action) {
          case MotionEvent.ACTION_SCROLL:
            x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
            y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
            SDLActivity.onNativeMouse(0, action, x, y);
            return true;

          case MotionEvent.ACTION_HOVER_MOVE:
            x = event.getX(0);
            y = event.getY(0);

            SDLActivity.onNativeMouse(0, action, x, y);
            return true;

          default:
            break;
        }

      default:
        break;
    }

    // Event was not managed
    return false;
  }
예제 #30
0
  @Override
  public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    Log.v("SDL", "onWindowFocusChanged(): " + hasFocus);

    if (SDLActivity.mBrokenLibraries) {
      return;
    }

    SDLActivity.mHasFocus = hasFocus;
    if (hasFocus) {
      SDLActivity.handleResume();
    }
  }